【问题标题】:Scraping images from url concurrently同时从 url 中抓取图像
【发布时间】:2014-02-15 13:29:50
【问题描述】:

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

目前我的代码是后者:

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 

我在想这个过程可以通过采用多线程方法来加速,但不确定如何。

谢谢

【问题讨论】:

标签: python multithreading beautifulsoup


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

    我已经为大量不同的网站编写了抓取工具(有些网站的数据量高达 8+ TB)。 Python 将难以在单个脚本上获得全线速率,最好的办法是使用适当的作业队列(例如 celery),然后运行多个工作器以实现并发。

    如果您不想要celery,那么另一种骇人听闻的方法是使用subprocess 调用curl/wget/axel 的多个实例,然后阻塞直到它们返回,检查退出代码,检查文件是否存在等。但是,如果您的脚本没有干净地退出,那么你最终会出现僵尸进程(即即使在你杀死脚本后下载仍在继续)。如果你不喜欢subprocess 的想法,那么你可以使用eventletgevent 之类的东西,但你不会在单个脚本上实现全线速,你必须运行多个工作人员。

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

    Python 是用于抓取大量数据的完美工具,您只需正确操作即可。

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

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

    值得一提的是threading+BeautifulSoup已经讨论过了;

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

    【讨论】:

      【解决方案3】:

      只需使用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()
      

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 2018-12-31
          • 1970-01-01
          • 1970-01-01
          • 2017-09-21
          • 1970-01-01
          • 2011-03-31
          • 2017-12-09
          • 1970-01-01
          • 2015-06-08
          相关资源
          最近更新 更多