[译]Tornado并发爬虫

译者说

Tornado 4.3于2015年11月6日发布,该版本正式支持Python3.5async/await关键字,并且用旧版本CPython编译Tornado同样可以使用这两个关键字,这无疑是一种进步。其次,这是最后一个支持Python2.6Python3.2的版本了,在后续的版本了会移除对它们的兼容。现在网络上还没有Tornado4.3的中文文档,所以为了让更多的朋友能接触并学习到它,我开始了这个翻译项目,希望感兴趣的小伙伴可以一起参与翻译,项目地址是tornado-zh on Github,翻译好的文档在Read the Docs上直接可以看到。欢迎Issues or PR。

示例 - 一个并发网络爬虫

Tornado的 tornado.queues 模块实现了异步生产者/消费者模式的协程, 类似于通过Python 标准库的 queue实现线程模式.

一个yield Queue.get 的协程直到队列中有值的时候才会暂停. 如果队列设置了最大长度yield Queue.put 的协程直到队列中有空间才会暂停.

一个Queue从0开始对完成的任务进行计数. Queue.put 加计数;Queue.task_done减少计数.

这里的网络爬虫的例子, 队列开始的时候只包含base_url. 当一个worker抓取到一个页面它会解析链接并把它添加到队列中, 然后调用Queue.task_done 减少计数一次. 最后, 当一个worker抓取到的页面URL都是之前抓取到过的并且队列中没有任务了.于是worker调用 Queue.task_done 把计数减到0. 等待 Queue.join 的主协程取消暂停并且完成.

    import time    from datetime import timedelta    try:        from HTMLParser import HTMLParser        from urlparse import urljoin, urldefrag    except ImportError:        from html.parser import HTMLParser        from urllib.parse import urljoin, urldefrag    from tornado import httpclient, gen, ioloop, queues

    base_url = 'http://www.tornadoweb.org/en/stable/'
    concurrency = 10


    @gen.coroutine
    def get_links_from_url(url):
        """Download the page at `url` and parse it for links.

        Returned links have had the fragment after `#` removed, and have been made
        absolute so, e.g. the URL 'gen.html#tornado.gen.coroutine' becomes
        'http://www.tornadoweb.org/en/stable/gen.html'.
        """
        try:
            response = yield httpclient.AsyncHTTPClient().fetch(url)
            print('fetched %s' % url)

            html = response.body if isinstance(response.body, str) \                else response.body.decode()
            urls = [urljoin(url, remove_fragment(new_url))                    for new_url in get_links(html)]        except Exception as e:
            print('Exception: %s %s' % (e, url))            raise gen.Return([])        raise gen.Return(urls)    def remove_fragment(url):
        pure_url, frag = urldefrag(url)        return pure_url    def get_links(html):
        class URLSeeker(HTMLParser):
            def __init__(self):
                HTMLParser.__init__(self)
                self.urls = []            def handle_starttag(self, tag, attrs):
                href = dict(attrs).get('href')                if href and tag == 'a':
                    self.urls.append(href)

        url_seeker = URLSeeker()
        url_seeker.feed(html)        return url_seeker.urls    @gen.coroutine
    def main():
        q = queues.Queue()
        start = time.time()
        fetching, fetched = set(), set()        @gen.coroutine
        def fetch_url():
            current_url = yield q.get()            try:                if current_url in fetching:                    return

                print('fetching %s' % current_url)
                fetching.add(current_url)
                urls = yield get_links_from_url(current_url)
                fetched.add(current_url)                for new_url in urls:                    # Only follow links beneath the base URL
                    if new_url.startswith(base_url):                        yield q.put(new_url)            finally:
                q.task_done()        @gen.coroutine
        def worker():
            while True:                yield fetch_url()

        q.put(base_url)        # Start workers, then wait for the work queue to be empty.
        for _ in range(concurrency):
            worker()        yield q.join(timeout=timedelta(seconds=300))        assert fetching == fetched
        print('Done in %d seconds, fetched %s URLs.' % (
            time.time() - start, len(fetched)))    if __name__ == '__main__':        import logging
        logging.basicConfig()
        io_loop = ioloop.IOLoop.current()
        io_loop.run_sync(main)

原文发布于微信公众号 - MoeLove(TheMoeLove)

原文发表时间:2016-01-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小白安全

妙用JavaScript绕过XSS过滤-----小白安全博客

基于DOM的XSS漏洞利用 Mavo框架会创建一个名为$url的对象,该对象能够为开发人员提供访问GET参数的便捷方法。例如,如果你想访问GET参数“x”...

489120
来自专栏大内老A

使命必达: 深入剖析WCF的可靠会话[协议篇](下)

在《上篇》中,我们认识了从序列创建到终止过程中消息交换的大致流程。接下来,我们进一步将关注点聚焦到单个小消息上,看看在整个基于序列的上下文中,不同类型的消息具有...

24980
来自专栏腾讯IVWEB团队的专栏

通过ffi在Node.js中调用动态链接库(.so/.dll文件)

由于公司许多公共的后台服务已经有了非常成熟的C/C++编写的API,以供应用程序调用,node.js作为在公司内新兴的后台runtime在调用这些公共服务的时候...

1.3K00
来自专栏Jimoer

JVM学习记录-类加载器

JVM设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外面去实现,以便让应用程序自己决定如何去获取所需要的...

7910
来自专栏逍遥剑客的游戏开发

Nebula3学习笔记(5): IO系统

19740
来自专栏大内老A

认识ASP.NET MVC的5种AuthorizationFilter

在总体介绍了筛选器及其提供机制(《深入探讨ASP.NET MVC的筛选器》)之后,我们按照执行的先后顺序对四种不同的筛选器进行单独介绍,首先来介绍最先执行的Au...

25460
来自专栏python学习路

二、路由、模板

一、路由系统 在settings.py文件中通过ROOT_URLCONF指定根级url的配置 urlpatterns是一个url()实例的列表 一个url()对...

34580
来自专栏Jackie技术随笔

I/O复用——select函数

select函数让进程告诉内核,等待数个事件,某个事件发生或者达到指定时间时,唤醒进程。

20340
来自专栏码神联盟

面试题 | 《Java面试题集》-- 第三套

varchar2分别在oracle的sql和pl/sql中都有使用,oracle 在sql参考手册和pl/sql参考手册中指出:oracle sql varch...

16620
来自专栏阿杜的世界

使用SA分析内存溢出问题背景例子程序方式方法实践参考资料

在阅读《Java性能调优指南》一书的最后,书中介绍了Serviceability Agent,并给出了一些排查问题的示例,我感觉看书不够深刻,因此自己在macO...

15420

扫码关注云+社区

领取腾讯云代金券