同时从网址抓取图像

时间:2014-01-24 14:03:14

标签: python multithreading beautifulsoup

我没有做太多线程,但想知道我是否可以同时在网页上保存图像而不是一次保存一个。

目前我的代码执行后者:

while pageCount <= 5:
soup = BeautifulSoup(urllib2.urlopen("http://www.url.../%d" % pageCount))

for link in soup.find_all("div", class_="photo"):
    pic = link.findAll('img')
    url = re.search("(?P<url>https?://[^\s]+\.(?:jpe?g))", str(pic)).group("url") 
    count +=1 
    urllib.urlretrieve(url,'C:\Desktop/images/pics%s.jpg' % count)
pageCount +=1 

我认为采用多线程方法可以加快这一过程,但不确定如何。

由于

4 个答案:

答案 0 :(得分:4)

scrapy正在并行执行,并准备好使用image download middleware

答案 1 :(得分:4)

Python中的多线程只会在IO因GIL而阻塞的点上使脚本更快,任何CPU / IO密集型应用程序都不会看到任何性能提升(如果有的话,它们可能会变慢)。

我为不同的网站编写了一些刮刀(有些网站的数据量大到8+ TB)。 Python很难在单个脚本上获得全线速率,最好的办法是使用正确的作业队列(例如celery),然后运行多个工作程序来实现并发。

如果你不想要celery,那么另一个hacky方法是使用subprocess来调用curl/wget/axel的多个实例然后阻塞直到它们返回,检查退出代码,检查文件但是如果你的脚本没有干净地退出,那么你最终会得到僵尸进程(即即使在你杀死脚本后下载也会继续)。如果您不喜欢subprocess的想法,那么您可以使用eventletgevent之类的内容,但是您无法在单个脚本上获得完整的线路费率,您将拥有运行多个工人。

某些网站具有速率限制,因此使用作业队列通常是解决此问题的一种好方法(即大量具有随机IP的EC2实例),每个网站上有X个工作人员以获得最大吞吐量。

Python是一个非常好的工具,用于抓取大量数据,你只需要正确地完成它。

此外,在许多情况下,pyquery比BeautifulSoup明显快于处理结果。至少,不要依赖BeautifulSoup库为您请求数据。使用python-requests之类的东西来获取结果,然后将其传递给解析器(即汤或pyquery等)。

如果您计划抓取/存储大量数据(例如处理作业和下载内容时的带宽优化),还可以考虑可扩展性。存储群集允许您向其API发送URL,并且他们可以为您下载内容。这可以通过下载然后将文件上传到后端来节省浪费带宽 - 这可以将带宽费用减少一半。

还值得一提的是,已经讨论过线程+ BeautifulSoup;

Urllib2 & BeautifulSoup : Nice couple but too slow - urllib3 & threads?

答案 2 :(得分:1)

如果您寻找DIY解决方案,请使用:

我认为你可以将整个soup.findall()的循环体映射到池中。

答案 3 :(得分:1)

只需使用pool,无论是线程还是多处理。

from multiprocessing.pool import ThreadPool
pool = ThreadPool(10) ## tweak this param


while pageCount <= 5:
   soup = BeautifulSoup(urllib2.urlopen("http://www.url.../%d" % pageCount))
   for link in soup.find_all("div", class_="photo"):
       pic = link.findAll('img')
       url = re.search("(?P<url>https?://[^\s]+\.(?:jpe?g))", str(pic)).group("url") 
       count +=1 
       _ = pool.apply_async(urllib.urlretrieve, (url,'C:\Desktop/images/pics%s.jpg' % count))
   pageCount +=1 


pool.close()
pool.join()