2

我想抓取一些网页并将其保存为 HTML。比如说,爬入数百个流行的网站,然后简单地保存它们的首页和“关于”页面。

我研究了很多问题,但没有从网络抓取或网络抓取问题中找到答案。

我应该使用什么库或工具来构建解决方案?或者是否有一些现有的工具可以处理这个问题?

4

6 回答 6

6

使用 Python 时,您可能会对mechanizeBeautifulSoup感兴趣。

Mechanize有点模拟浏览器(包括代理选项、伪造浏览器标识、页面重定向等),并允许轻松获取表单、链接……虽然文档有点粗糙/稀疏。

一些示例代码(来自 mechanize 网站)给你一个想法:

import mechanize
br = mechanize.Browser()
br.open("http://www.example.com/")
# follow second link with element text matching regular expression
html_response = br.follow_link(text_regex=r"cheese\s*shop", nr=1)
print br.title()
print  html_response

BeautifulSoup允许非常容易地解析 html 内容(您可以使用 mechanize 获取),并且支持正则表达式。

一些示例代码:

from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup(html_response)

rows = soup.findAll('tr')
for r in rows[2:]:  #ignore first two rows
    cols = r.findAll('td')
    print cols[0].renderContents().strip()    #print content of first column

因此,上面这 10 行几乎是复制粘贴准备打印网站上每个表格行的第一列的内容。

于 2011-10-11T07:57:30.697 回答
4

这里真的没有好的解决方案。你是对的,因为你怀疑 Python 可能是最好的开始方式,因为它对正则表达式的支持非常强大。

为了实现这样的事情,强大的 SEO(搜索引擎优化)知识会有所帮助,因为有效地优化搜索引擎的网页会告诉您搜索引擎的行为方式。我将从SEOMoz 之类的网站开始。

至于识别“关于我们”页面,您只有 2 个选项:

a) 对于每个页面,获取关于我们页面的链接并将其提供给您的爬虫。

b) 解析页面的所有链接以查找某些关键字,例如“关于我们”、“关于”、“了解更多”或其他任何内容。

在使用选项 b 时,请小心,因为您可能会陷入无限循环,因为网站将多次链接到同一页面,特别是如果链接位于页眉或页脚中,则页面甚至可能链接回自身。为避免这种情况,您需要创建已访问链接的列表,并确保不要重新访问它们。

最后,我建议让您的爬虫尊重robot.txt文件中的说明,并且最好不要遵循标记的链接rel="nofollow",因为这些链接主要用于外部链接。再次,通过阅读 SEO 来了解这一点以及更多信息。

问候,

于 2011-10-11T07:59:51.097 回答
3

Try out scrapy. It is a web scraping library for python. If a simple python-script is expected, try urllib2 in python.

于 2011-10-11T07:55:22.260 回答
3

Python ==> Curl <--爬虫的最佳实现

下面的代码可以在一个不错的服务器上在 300 秒内爬取 10,000 个页面。

#! /usr/bin/env python
# -*- coding: iso-8859-1 -*-
# vi:ts=4:et
# $Id: retriever-multi.py,v 1.29 2005/07/28 11:04:13 mfx Exp $

#
# Usage: python retriever-multi.py <file with URLs to fetch> [<# of
#          concurrent connections>]
#

import sys
import pycurl

# We should ignore SIGPIPE when using pycurl.NOSIGNAL - see
# the libcurl tutorial for more info.
try:
    import signal
    from signal import SIGPIPE, SIG_IGN
    signal.signal(signal.SIGPIPE, signal.SIG_IGN)
except ImportError:
    pass


# Get args
num_conn = 10
try:
    if sys.argv[1] == "-":
        urls = sys.stdin.readlines()
    else:
        urls = open(sys.argv[1]).readlines()
    if len(sys.argv) >= 3:
        num_conn = int(sys.argv[2])
except:
    print "Usage: %s <file with URLs to fetch> [<# of concurrent connections>]" % sys.argv[0]
    raise SystemExit


# Make a queue with (url, filename) tuples
queue = []
for url in urls:
    url = url.strip()
    if not url or url[0] == "#":
        continue
    filename = "doc_%03d.dat" % (len(queue) + 1)
    queue.append((url, filename))


# Check args
assert queue, "no URLs given"
num_urls = len(queue)
num_conn = min(num_conn, num_urls)
assert 1 <= num_conn <= 10000, "invalid number of concurrent connections"
print "PycURL %s (compiled against 0x%x)" % (pycurl.version, pycurl.COMPILE_LIBCURL_VERSION_NUM)
print "----- Getting", num_urls, "URLs using", num_conn, "connections -----"


# Pre-allocate a list of curl objects
m = pycurl.CurlMulti()
m.handles = []
for i in range(num_conn):
    c = pycurl.Curl()
    c.fp = None
    c.setopt(pycurl.FOLLOWLOCATION, 1)
    c.setopt(pycurl.MAXREDIRS, 5)
    c.setopt(pycurl.CONNECTTIMEOUT, 30)
    c.setopt(pycurl.TIMEOUT, 300)
    c.setopt(pycurl.NOSIGNAL, 1)
    m.handles.append(c)


# Main loop
freelist = m.handles[:]
num_processed = 0
while num_processed < num_urls:
    # If there is an url to process and a free curl object, add to multi stack
    while queue and freelist:
        url, filename = queue.pop(0)
        c = freelist.pop()
        c.fp = open(filename, "wb")
        c.setopt(pycurl.URL, url)
        c.setopt(pycurl.WRITEDATA, c.fp)
        m.add_handle(c)
        # store some info
        c.filename = filename
        c.url = url
    # Run the internal curl state machine for the multi stack
    while 1:
        ret, num_handles = m.perform()
        if ret != pycurl.E_CALL_MULTI_PERFORM:
            break
    # Check for curl objects which have terminated, and add them to the freelist
    while 1:
        num_q, ok_list, err_list = m.info_read()
        for c in ok_list:
            c.fp.close()
            c.fp = None
            m.remove_handle(c)
            print "Success:", c.filename, c.url, c.getinfo(pycurl.EFFECTIVE_URL)
            freelist.append(c)
        for c, errno, errmsg in err_list:
            c.fp.close()
            c.fp = None
            m.remove_handle(c)
            print "Failed: ", c.filename, c.url, errno, errmsg
            freelist.append(c)
        num_processed = num_processed + len(ok_list) + len(err_list)
        if num_q == 0:
            break
    # Currently no more I/O is pending, could do something in the meantime
    # (display a progress bar, etc.).
    # We just call select() to sleep until some more data is available.
    m.select(1.0)


# Cleanup
for c in m.handles:
    if c.fp is not None:
        c.fp.close()
        c.fp = None
    c.close()
m.close()
于 2011-11-29T13:02:29.263 回答
2

如果您要构建一个爬虫,您需要(Java 特定):

  1. 了解如何使用 java.net.URL 和 java.net.URLConnection 类或使用 HttpClient 库
  2. 了解 http 请求/响应标头
  3. 了解重定向(HTTP、HTML 和 Javascript)
  4. 了解内容编码(字符集)
  5. 使用好的库来解析格式错误的 HTML(例如,cyberNecko、Jericho、JSoup)
  6. 向不同主机发出并发 HTTP 请求,但确保每 ~5 秒向同一主机发出不超过一个
  7. 持久化您已获取的页面,因此如果它们不经常更改,则无需每天重新获取它们(HBase 可能很有用)。
  8. 一种从当前页面中提取链接到下一个爬取的方法
  9. 服从 robots.txt

还有一堆其他的东西。

这并不难,但是有很多复杂的边缘情况(例如重定向、检测编码(结帐 Tika))。

对于更多基本要求,您可以使用 wget。Heretrix 是另一种选择,但也是另一个需要学习的框架。

可以使用各种启发式方法来识别关于我们的页面:

  1. 入站链接文本
  2. 页面标题
  3. 页面内容
  4. 网址

如果您想对其进行更多量化,则可以使用机器学习和分类器(也许是贝叶斯)。

保存首页显然更容易,但是首页重定向(有时到不同的域,并且通常在 HTML 元重定向标记甚至 JS 中实现)非常常见,因此您需要处理这个问题。

于 2011-10-11T08:07:52.390 回答
1

Heritrix的学习曲线有点陡峭,但可以配置为仅抓取主页和“看起来”(使用正则表达式过滤器)关于页面的页面。

更多开源Java(web)爬虫:http: //java-source.net/open-source/crawlers

于 2011-10-11T07:58:57.937 回答