使用多线程队列+ urllib3进行刮擦会严重减速

时间:2016-02-08 20:43:20

标签: multithreading python-3.x web-scraping queue urllib3

我试图在尽可能短的时间内抓取大量包含JSON格式数据的URL(大约3百万个)。为此,我有一个使用Queue,Multithreading和Urllib3的Python代码(python 3)。在前3分钟内一切正常,然后代码开始减速,然后它似乎完全陷入困境。我已经阅读了我在这个问题上可以找到的所有内容,但不幸的是,解决方案似乎需要一种远远超出我的知识。

我试图限制线程数:它没有修复任何东西。我还尝试限制队列的maxsize并更改套接字超时,但它也没有任何帮助。远程服务器没有阻止我,也没有将我列入黑名单,因为我可以随时重新启动我的脚本,并且在开始时会有很好的结果(代码在相当随机的时间开始变慢)。此外,有时我的互联网连接似乎被削减 - 因为我不能在任何网站上冲浪 - 但这个特定的问题并不是每次都出现。

这是我的代码(请告诉我,我是个笨蛋):

#!/usr/bin/env python
import urllib3,json,csv
from queue import Queue
from threading import Thread

csvFile =  open("X.csv",  'wt',newline="")
writer  =  csv.writer(csvFile,delimiter=";")
         writer.writerow(('A','B','C','D'))

def do_stuff(q):
    http = urllib3.connectionpool.connection_from_url('http://www.XXYX.com/',maxsize=30,timeout=20,block=True)

    while True:

        try:

            url = q.get()
            url1 = http.request('GET',url)      

            doc = json.loads(url1.data.decode('utf8'))

            writer.writerow((doc['A'],doc['B'], doc['C'],doc['D']))

        except:
             print(url)

        finally:
            q.task_done()

q = Queue(maxsize=200)
num_threads = 15

for i in range(num_threads):
    worker = Thread(target=do_stuff, args=(q,))
    worker.setDaemon(True)
    worker.start()

for x in range(1,3000000):
    if x < 10:
        url = "http://www.XXYX.com/?i=" + str(x) + "&plot=short&r=json"
    elif x < 100:
        url = "http://www.XXYX.com/?i=tt00000" + str(x) + "&plot=short&r=json"
    elif x < 1000:
        url = "http://www.XXYX.com/?i=0" + str(x) + "&plot=short&r=json"
    elif x < 10000:
        url = "http://www.XXYX.com/?i=00" + str(x) + "&plot=short&r=json"
    elif x < 100000:
        url = "http://www.XXYX.com/?i=000" + str(x) + "&plot=short&r=json"
    elif x < 1000000:
        url = "http://www.XXYX.com/?i=0000" + str(x) + "&plot=short&r=json"
    else:
        url = "http://www.XXYX.com/?i=00000" + str(x) + "&plot=short&r=json"

    q.put(url)

q.join()    
csvFile.close()
print("done")

1 个答案:

答案 0 :(得分:0)

正如shazow所说,这不是线程的问题,而是每个线程从服务器获取数据的超时。尝试在代码中包含一些时间:

finally:
    sleep(50)
    q.task_done()

它也可以通过生成自适应超时来改进,例如,您可以测量您成功获得的数据量,如果该数量减少,则增加睡眠时间,反之亦然