【问题标题】:accessing a list of urls via Multi-processing in Python2.7通过 Python2.7 中的多处理访问 url 列表
【发布时间】:2016-04-24 04:53:57
【问题描述】:

我一直在玩多处理,我的代码在查看较小的数字时可以工作,但是当我想运行更大的样本时,会发生 2 件事:代码锁定或我收到以下错误消息:“urlopen 错误 [Errno 10054] 现有连接被远程主机强行关闭”。我不知道如何让它工作。谢谢。

    from multiprocessing import cpu_count
    import urllib2
    from bs4 import BeautifulSoup
    import json
    import timeit
    import socket
    import errno

    def parseWeb(id):
        url = 'https://carhood.com.au/rent/car_detail/'+str(id)+'/'
        hdr = {'Accept': 'text/html,application/xhtml+xml,*/*',"user-agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36"}

        html = urllib2.urlopen(url).read()
        soup=BeautifulSoup(html,"lxml")
        car=soup.find("h1",{"class":"intro-title intro-title-tertiary"}).text

        return car

    if __name__ == '__main__':
        start = timeit.default_timer()
        pool = Pool(cpu_count()*100)
#This works for me when xrange(1,70)
        results=pool.map(parseWeb,xrange(1,400))
        print results
##I've tried this as a solution but it didn't work
        ##    startNum=1
##    endNum=470
##    for x in range(startNum,endNum,70):
##        print x
##        results=pool.map(parseWeb,xrange(startNum,x))
##        print results
##        startNum=x  
        stop = timeit.default_timer()
        print stop - start

【问题讨论】:

  • 每个 CPU 核心产生 100 个进程真的合理吗?我不认为它是。这对网站所有者也很刻薄。
  • 我想有两件事我应该注意:1)我只是想学习如何去做,很高兴听到更合理的进程号是什么,因此我要问和 2 ( .
  • 好的,我的建议是:我认为同时向一个域发出超过 3 或 4 个请求是过多的,应该避免。如果可能,请尝试在域中查找可让您通过单个请求下载更多数据的资源。不要产生数百个进程。
  • 你应该真正检查一个站点是否有一个/robots.txt 文件,并在你爬遍它之前尊重它的内容。

标签: python python-2.7 multiprocessing urllib2


【解决方案1】:

John Zwinck 在 cmets 问题中的建议非常中肯。

部分问题是您无法控制接收服务器。当您放置过多的进程时,您会强制另一端的服务器找出正确的处理方式您的所有请求一次。这会导致您的进程闲置在那里等待服务器在某个时候返回它们 - 因为 pool.map() 仅在您的所有进程完成时才完成(这是一个 阻塞 调用),这意味着只要服务器需要为每个人提供服务,您就可以等待。

现在一切都取决于服务器。

  • 服务器可以选择将其资源用于一个一个地为您的所有请求提供服务 - 这实际上意味着您的请求现在在队列中等待,没有比您有任何优势只是逐个发送您的请求。单线程服务器可以像这样建模,尽管它们的主要加速来自于它们是异步的并且在请求和请求之间快速跳转。

  • 某些服务器通常具有少量进程或线程,这些进程或线程会产生大量子线程,这些子线程都一一处理传入的请求 - 例如 Apache 服务器,例如 starts off with 2 dedicated processes with 25 threads each,因此理论上它可以处理50 个并发请求,并且可以根据配置进行扩展。此时它将为尽可能多的服务提供服务,并将您剩余的多余请求搁置或拒绝服务。

  • 如果某些服务器威胁到系统过载或达到内部超时,它们会简单地终止或关闭连接。后者更有可能,也更经常遇到。

另一方面,您自己的 CPU 内核无法处理您要求它们执行的操作。 一个内核可以同时处理 一个 线程时间 - 当我们谈到并行性时,我们实际上是在谈论同时处理一个线程的多个内核。具有大量较小线程的进程可以将这些线程分布在不同的 CPU 内核中,因此您可以从中受益。

但是您有一百个进程,每个进程都会引发一个阻塞 I/O 调用(urlopenblocking)。如果该 I/O 调用立即得到响应,那么到目前为止一切都很好 - 如果没有,那么现在其他进程正在等待这个进程完成,占用了宝贵的 CPU 内核。您已成功地将 waiting 引入到您想要明确避免等待的系统中。如果您将此问题与您在接收服务器上引起的压力相结合,您会发现许多延迟源于打开的连接。

解决方案

有很多解决方案,但在我自己看来,它们都归结为同一件事:

  • 避免阻塞调用。使用触发请求的解决方案,将负责该请求的线程置于睡眠状态并退出调度程序运行队列,并在注册事件时将其唤醒。

  • 使用异步对你有利。一个线程可以在不阻塞的情况下发出多个请求,您只需要能够智能地处理响应,因为它们一个一个进来。您甚至可以将响应传递给没有做任何工作的其他线程(例如使用Queue)。诀窍是让它们无缝地协同工作。

multiprocessing 虽然是处理进程的一个很好的解决方案,但不是处理 HTTP 请求和进程的适当行为之间的交互的捆绑解决方案。这是您通常必须自己编写的逻辑,如果您对urlopen 的工作方式有更大的控制权,可以完成 - 您必须想办法确保urlopen不会阻止,或者至少愿意在发送请求后立即订阅事件通知。

当然,这一切都可以完成 - 但网页抓取是一个已解决的问题,无需重写轮子。

相反,有几个选项已经过尝试和测试:

  • asyncio 是 Python 3.5 的标准。虽然不是一个成熟的 HTTP 服务,但它为 I/O 绑定操作提供异步支持。您可以使用 aiohttp 发出 HTTP 请求。这是一个tutorial,关于如何使用它进行刮擦。

  • Scrapy 在 Python 2.7 和 Python 3 上是可行的。它使用Twistedasyncio 的非标准先行者和快速网络请求的首选工具。我提到 Scrapy 而不是 Twisted 只是因为 Scrapy 已经为您处理了底层架构 [可以阅读 here] - 如果您愿意,您当然应该探索 Twisted 以了解底层系统。它是我将在此处提及的所有解决方案中最实用的,但根据我的经验,它也是性能最高的。

  • grequests 是流行的requests 库的扩展(顺便提一下,它优于urllib2,应该在任何机会使用)以支持所谓的协程:可以暂停和恢复的线程在它们执行的多个点,如果您希望线程在等待 I/O 响应时工作,这是非常理想的。 grequests 建立在 gevent(协程库)之上,让您可以在单个线程中发出多个请求,并按照自己的节奏处理它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-14
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 2019-10-14
    • 1970-01-01
    • 2019-03-26
    相关资源
    最近更新 更多