【问题标题】:need design suggestions for an efficient webcrawler that is going to parse 8M pages - Python [closed]需要用于解析 8M 页面的高效网络爬虫的设计建议 - Python [关闭]
【发布时间】:2011-07-19 17:22:11
【问题描述】:

我要开发一个小爬虫,它会从同一个网站获取很多页面,所有的请求都是 url 的 ID 号的变化。

我需要将我要解析的所有数据保存到一个csv中(没什么花哨的),最多我会爬大约6M-8M的页面,其中大部分不包含我想要的数据,我知道那里我需要解析大约 400K 个页面,它们的结构都相似,我无法避免爬取所有的 url。

这就是我获取数据时页面的外观 - http://pastebin.com/3DYPhPRg

那是我没有得到数据的时候 - http://pastebin.com/YwxXAmih

数据保存在 td 内部的 span 中 -

I need the data between ">" and "</span>".

<span id="lblCompanyNumber">520000472</span></td>
<span id="lblCompanyNameHeb">חברת החשמל לישראל בעמ</span></td>
<span id="lblStatus">פעילה</span></td>
<span id="lblCorporationType">חברה ציבורית</span></td>
<span id="lblGovCompanyType">חברה  ממשלתית</span></td>
<span id="lblLimitType">מוגבלת</span></td>
etc'

从文档中解析出来并不难。

问题是获取url并解析它们需要几天的时间,它会消耗大量内存,我认为它会时不时地崩溃,这对我来说非常危险,它可以'除非它不能再运行,否则不会崩溃。

我想过-

 - fetching a url (urllib2)
      - if there's an error - move next (if it'll happen 5 times - I stop and save errors to log)
 - parse the html (still don't know whats best - BeautifulSoup \ lxml \
    scrapy \ HTMLParser etc')
      - if it's empty (lblCompanyNumber will be empty) save the ID in the emptyCsvFile.csv
 - else: save the data to goodResults.csv

问题是 -

  1. 我应该使用哪些数据类型以提高效率和速度(对于我解析的数据和获取的内容)?
  2. 我应该使用哪个 HTML 解析库?也许正则表达式? span id 是固定的,当有数据时不会改变(再次强调,高效、速度、简单)
  3. 保存到文件,长时间保存文件的句柄等' - 有没有一种方法可以节省资源并更有效地保存数据? (至少 40 万行)
  4. 任何其他我没有想到但需要处理的事情,也许还有一些优化技巧 :)

我想到的另一个解决方案是使用 wget,将所有页面保存到磁盘,然后删除所有具有相同 md5sum 的空文档的文件,唯一的问题是我没有保存空 ID。

顺便说一句,我需要使用 py2exe 并用它制作一个 exe,所以像 scrapy 这样的东西在这里可能很难使用(已知会导致 py2exe 出现问题)。

谢谢!

【问题讨论】:

    标签: python html-parsing web-crawler


    【解决方案1】:

    我将 httplib2 用于这种事情,因为 Python 标准库例程中应该存在内存泄漏。此外,httplib2 可以配置为保留缓存,如果您必须重新启动并重做一些页面,这可能会很有用。

    我只浏览了 170 万页以及来自另一台服务器的大约 200000 页,所以我无法评论您期望的数量。

    但是我使用带有主题交换和持久消息队列 (delivery_mode=2) 的 AMQP 来推动这一切。这会将 ny id 输入到使用 httplib2 的 worker 中,并确保检索到每个 id。我使用内存缓存跟踪它们,该内存缓存使用磁盘上的 Tokyo Tyrant 哈希表进行持久化。我能够关闭并重新启动工作人员并在机器之间移动它们而不会丢失任何 ID。在我杀死它以修补它之前,我已经让工人一次运行了两三个星期。

    另外,我使用 lxml 来解析响应,因为它很快。

    哦,在成功检索和处理页面后,我将 id 作为消息发布到已完成队列。然后,我手动将消息从该队列中复制出来,并将其与输入列表进行比较,以确保整个过程是可靠的。

    对于 AMQP,我使用 amqplib 和 RabbitMQ 作为代理。现在我建议看看 haigha 的 AMQP。尽管它的文档很少,但它的模型紧跟 AMQP 0.9.1 规范文档,因此您可以使用这些文档来确定选项等。

    @YSY:我无法剪切和粘贴代码,因为我是在工作中完成的,但这并没有什么特别之处。只是一个带有 try: except: 的循环,包裹着 http 请求。像这样的:

    retries = 5
    while retries > 0:
        requestSucceeded = True # assume the best
        try:
            resp, content = h.request("http://www.example.com/db/1234567")
            if resp is None:
                requestSucceeded = False
                log.warn ("1234567: no http response")
            elif resp.status != 200:
                requestSucceeded = False
                log.warn ("1234567: replied with {0:d}".format(resp.status))
        except Exception as e:
            requestSuceeded = False
            log.warn("1234567: exception - " + str(e)) 
        if not requestSucceeded:
            time.sleep(30)
            retries -= 1
        else:
            retries = 0
    if requestSucceded:
        process_request()
        ack_message()
    

    llop 处理两种类型的故障,一种是 HTTP 服务器与我们通信但未返回回复,另一种是出现异常,可能是网络错误或其他任何原因。您可以更复杂,并以不同的方式处理不同的故障情况。但这通常有效。调整睡眠时间并重试,直到获得超过 90% 的成功率,然后再处理其余的。我相信我现在正在使用半小时的睡眠和 3 次重试,或者可能是 15 分钟的睡眠。真的不重要。

    在完整运行后,我会处理结果(日志和已完成消息的列表)以确保他们同意,并且任何未能检索到的文档,我会在放弃前改天再试一次。当然,如果我能想到办法,我会扫描日志寻找类似的问题并调整我的代码来处理它们。

    或者你可以用谷歌搜索“scrapy”。这可能对你有用。就个人而言,我喜欢使用 AMQP 来控制整个过程。

    【讨论】:

    • 听起来你写了一个非常令人印象深刻和强大的爬虫,事实上我在 urllib2 上遇到了一些奇怪的问题(它发送了 GET,没有等待响应并跳到下一个请求(用wireshark见过几次)。我也会尝试使用httplib2。顺便说一句 - 看到你的代码片段会很棒(我相信我们中的许多人都会学到很多看到和测试它)。跨度>
    • 你能解释一下 - “我能够关闭并重新启动工作人员并在机器之间移动它们而不会丢失任何 ID”你是怎么做到的?你能展示一个样本“工人”吗?你是如何验证你得到每个请求的响应的?有一些事情无法监控导致服务器错误。
    • 如果您在发送消息时使用delivery-mode=2,那么它会一直留在队列中,直到工作人员收到并确认它。直到我成功下载并处理了消息中 id 的页面,我的代码才确认消息。确认消息后,我将 id 记录在已完成任务列表中,以与原始任务列表进行比较。当然,如果 Web 服务器从不响应(我每隔 30 分钟重试 5 次),那么您无能为力。
    • 我真的很想看看你是如何实现获取 url 并在失败时重试的代码,你是保存给你错误的 id 并继续运行还是停止爬取并等待?
    猜你喜欢
    • 2011-05-19
    • 1970-01-01
    • 2015-07-18
    • 2018-01-26
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    • 2011-07-07
    相关资源
    最近更新 更多