Skip to content

Latest commit

 

History

History
50 lines (41 loc) · 3.04 KB

使用进程池实现网络爬虫.md

File metadata and controls

50 lines (41 loc) · 3.04 KB

##使用ProcessPoolExecutor模块设计网络爬虫 正如concurrent.futures模块提供的ThreadPoolExecutor类方便了操控多线程程序,多进程同样有一个类叫ProcessPoolExecutor。ProcessPoolExecutor类同样由concurrent.futures包提供,我们将使用该类执行我们的网络爬虫程序。为了完成这个任务,我们创建了一个叫process_pool_executor_web_crawler.py的python模块。

代码要导入的包如之前章节中我们介绍的,如requests, Manager模块等等。对于任务的定义,我们延用上章线程程序中的代码,只是改动其中小部分代码,还有一点不一样,进程程序中,我们向任务函数传入参数,而不是使用全局变量。代码如下所示:

group_urls_task函数定义如下:

def group_urls_task(urls, result_dict, html_link_regex)

crawl_task函数定义如下:

def crawl_task(url, html_link_regex)

现在我们再来看下面一小部分代码,与前一章比有了细微的变化。在main函数中,我们获得Manager类的实例,该实例使得我们获得可以被多进程共享的queue和dict。我们使用Manager.Queue()方法获得queue实例来存储我们将要爬得的url。使用Manager.dict()方法获取dict,来存储爬虫的结果。下面的代码将展示上面的定义:

if __name__ == '__main__':
    manager = Manager()
    urls = manager.Queue()
    urls.put("http://br.bing.com/")
    urls.put("https://github.com")
    result_dict = manager.dict()

接着,我们将定义爬虫程序中将要用到的正则表达式和介绍如何获取机器的cpu个数,程序如下:

html_link_regex = \
        re.compile('<a\s(?:.*?\s)*?href=[\'"](.*?)[\'"].*?>')
number_of_cpus = cpu_count()

最后一块代码中,我们会注意到concurrent.futures模块中各API签名具有很强的一致性。下面的代码正是我们上章使用ThreadPoolExecutor模块时使用到的。我们只需把ThreadPoolExecutor变为ProcessPoolExecutor,就能改变程序内部行为并解决计算密集型进程的GIL问题。注意下面的程序,创建ProcessPoolExecutor时会根据机器cpu数限定进程的数目。第一个exucutor是为了收集将被爬的URL,把这些URLs保存在一个字典中,key为url而value为None。第二个executor执行爬虫程序。
首先是第一个executor:

with concurrent.futures.ProcessPoolExecutor(max_workers=number_of_cpus) as group_link_processes:
        for i in range(urls.qsize()):
            group_link_processes.submit(group_urls_task, urls, result_dict, html_link_regex)

第二个executor程序如下:

with concurrent.futures.ProcessPoolExecutor(max_workers=number_of_cpus) as crawler_link_processes:
        future_tasks = {crawler_link_processes.submit(crawl_task, url, html_link_regex): url for url in result_dict.keys()}
        for future in concurrent.futures.as_completed(future_tasks):
            result_dict[future.result()[0]] = future.result()[1]

程序运行结果如下图: