【问题标题】:web crawler cannot exceed about 1MB/sec speed网络爬虫不能超过大约 1MB/秒的速度
【发布时间】:2013-11-03 20:29:03
【问题描述】:

我正在构建一个网络爬虫,它可以从数百万个域的列表中获取 1-3 页,我正在使用多线程 Python,我尝试过使用 httplib、httplib2、urllib、urllib2、urllib3、requests 和 curl(最快的)以及扭曲和scrapy,但它们都不允许我使用超过大约10 mbit的带宽(我有60 mbit的速度),通常最多可以使用100-300个线程,然后它导致请求失败。我也遇到了 php/curl 的这个问题。我有一个刮板,它可以从谷歌加上带有 urllib3 和线程模块(Python)的页面,并且最大化我的 100mbit 连接(我相信这可能是因为它正在重新使用同一个主机的开放套接字,而谷歌有一个快速网络响应)

这是我使用 pycurl 的脚本之一的示例,我正在从包含 url 的 csv 文件中读取 url。

import pycurl
from threading import Thread
from Queue import Queue
import cStringIO


def get(readq,writeq):
    buf = cStringIO.StringIO()
    while True:
        url=readq.get()

        c = pycurl.Curl()
        c.setopt(pycurl.TIMEOUT, 15)
        c.setopt(pycurl.FOLLOWLOCATION, 1)
        c.setopt(pycurl.USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0')
        c.setopt(c.WRITEFUNCTION, buf.write)
        c.setopt(c.URL, url)
        try:
            c.perform()
            writeq.put(url+'  '+str(c.getinfo(pycurl.HTTP_CODE)))
        except:
            writeq.put('error  '+url)
print('hi')
readq=Queue()
writeq=Queue()

import csv
reader=csv.reader(open('alldataunq2.csv'))
sites = []
ct=0
for l in reader:
    if l[3] != '':
        readq.put('http://'+l[3])
        ct+=1
        if ct > 100000:
            break

t=[]
for i in range(100):
    Thread(target=get,args=(readq,writeq)).start()

while True:
    print(writeq.get())

瓶颈肯定是网络 IO,因为我的处理器/内存几乎没有被使用。有没有人成功编写过能够使用完整 100mbit 或更多连接的类似爬虫?

非常感谢任何关于如何提高抓取代码速度的意见

【问题讨论】:

  • 很可能是您的 ISP 检测到您经常 ping 到许多不同的地址,并决定限制您的 Internet 连接...您在随后的正常 Internet 访问期间达到什么速度?
  • 我已经对此进行了测试,在停止运行脚本后我可以立即使用 speedtest.net 达到 55-60 mbit 的速度但是我注意到在远程服务器上运行我的脚本时我的速度更快(也许网络连接更适合大量请求)我正在测试以下建议,到目前为止,我已经能够使用 scrapy-redis 在服务器(不使用我的家庭互联网连接)上实现 5MB/秒的速度点它似乎受到服务器的 cpu 的限制,所以这似乎是一个解决方案,我会在进行更多测试后更新这篇文章

标签: python web-scraping twisted scrapy urllib3


【解决方案1】:

在优化抓取速度时需要牢记几个因素。

连接位置

为了有效地重复使用连接,您需要确保为同一网站重复使用连接。如果您等待太久才能再次访问较早的主机,则连接可能会超时,这是不好的。打开新套接字是一项相对昂贵的操作,因此您希望不惜一切代价避免它。实现此目的的一个简单的启发式方法是按主机对下载目标进行排序并一次下载一个主机,但随后您会遇到下一个问题...

在主机之间分散负载

并非所有主机都有胖管道,因此您需要同时访问多个主机 - 这也有助于避免过多地向单个主机发送垃圾邮件。这里一个好的策略是拥有多个工作人员,每个工作人员一次专注于一个主机。这样,您可以在每个工作人员的上下文中控制每个主机的下载速率,并且每个工作人员将维护自己的连接池以重用来自的连接。

工人专业化

破坏吞吐量的一种方法是将数据处理例程(解析 HTML、提取链接等)与获取例程混合使用。一个好的策略是在获取工作人员中做最少的处理工作,并简单地保存数据以供一组单独的工作人员稍后提取和处理(甚至可能在另一台机器上)。

牢记这些事情,你应该能够从你的连接中挤出更多的东西。一些不相​​关的建议:考虑使用wget,您会惊讶于它在执行简单爬取时的效率(它甚至可以从巨大的清单文件中读取)。

【讨论】:

  • -1 表示无偿“使用我最喜欢的语言”在其他合理的答案中
  • @Jean-PaulCalderone 已编辑。实际上,Python 是我最喜欢的语言。答案的内容并不是真正特定于 Python,老实说,我认为 Go 将是一个更好的工具(作为一个在 Python 中实现了 webcrawler 并专门为上述任务编写了 urllib3/workerpool 的人),但我希望这让您满意。以后请记住,投反对票意味着“无用”。
  • 呃……什么?如果@shazow 的整个帖子都是“使用 golang”,也许?但是作为一篇内容丰富的帖子末尾的一句话建议?
【解决方案2】:

我认为在进行网络抓取时,您无法期望获得接近互联网连接最大吞吐量的任何地方。

抓取(以及一般的网页浏览)涉及发出大量小请求。大部分时间都花在了连接的建立和拆除上,以及在远程端等待开始交付您的内容。我猜想主动下载内容所花费的时间大概在 50% 左右。如果您正在下载一堆大文件,那么我认为您会看到更好的平均吞吐量。

【讨论】:

    【解决方案3】:

    scrapy-redis 试试scrapy。

    您必须调整settingsCONCURRENT_REQUESTSCONCURRENT_REQUESTS_PER_DOMAINCONCURRENT_REQUESTS_PER_IP。还要确保你有DOWNLOAD_DELAY = 0AUTOTHROTTLE_ENABLED = False

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多