我正在构建一个网络爬虫,它可以从数百万个域的列表中获取 1-3 页,我正在使用带有多线程的 Python,我已经尝试使用 httplib、httplib2、urllib、urllib2、urllib3、requests 和curl(最快的)以及twisted和scrapy,但它们都不允许我使用超过大约10兆位的带宽(我有60兆位的速度),通常最大值约为100-300个线程,之后它导致请求失败。我的 php/curl 也遇到过这个问题。我有一个抓取器,它使用 urllib3 和 Threads 模块(Python)从 google plus 页面抓取,并且最大程度地提高了我的 100mbit 连接(我相信这可能是因为它正在重复使用具有相同主机的开放套接字,并且 google 有一个快速的网络响应)
这是我使用 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 或更多连接的类似抓取工具?
非常感谢任何有关如何提高抓取代码速度的意见
最佳答案
优化抓取速度时需要牢记几个因素。
连接位置
为了有效地重复使用连接,您需要确保重复使用同一网站的连接。如果您等待太长时间才再次访问较早的主机,连接可能会超时,这并不好。打开新套接字是一项相对昂贵的操作,因此您希望不惜一切代价避免它。实现这一目标的一种天真的启发式方法是按主机对下载目标进行排序并一次下载一个主机,但随后您会遇到下一个问题...
在主机之间分散负载
并非所有主机都有胖管道,因此您需要同时访问多个主机 - 这也有助于避免向单个主机发送太多垃圾邮件。一个好的策略是拥有多个工作人员,每个工作人员一次专注于一台主机。通过这种方式,您可以控制每个工作线程上下文中每个主机的下载速率,并且每个工作线程将维护自己的连接池以重用来自的连接。
worker 特化
破坏吞吐量的一种方法是将数据处理例程(解析 HTML、提取链接等)与获取例程混合在一起。这里一个好的策略是在获取工作线程中执行最少的处理工作,并简单地将数据保存给一组单独的工作线程以便稍后获取和处理(甚至可能在另一台机器上)。
牢记这些事情,您应该能够从联系中获得更多 yield 。一些不相关的建议:考虑使用 wget
,您会惊讶于它在执行简单爬行时的效率(它甚至可以从巨大的 list 文件中读取)。
关于python - 网络爬虫的速度不能超过约1MB/秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19757721/