【问题标题】:How to use python-requests and event hooks to write a web crawler with a callback function?如何使用 python-requests 和 event hooks 编写带有回调函数的网络爬虫?
【发布时间】:2012-08-27 14:01:42
【问题描述】:

我最近看了python-requests 模块,我想用它写一个简单的网络爬虫。给定一组起始 url,我想编写一个 Python 函数,在起始 url 的网页内容中搜索其他 url,然后再次调用相同的函数作为回调,并将新的 url 作为输入,依此类推。起初,我认为event hooks 将是用于此目的的正确工具,但它的文档部分非常稀疏。在another page 上,我读到用于事件挂钩的函数必须返回传递给它们的相同对象。所以事件挂钩对于这类任务显然是不可行的。或者我只是没有做对......

这是我想做的一些伪代码(借自一个伪 Scrapy 蜘蛛):

import lxml.html    

def parse(response):
    for url in lxml.html.parse(response.url).xpath('//@href'):
        return Request(url=url, callback=parse)

谁能告诉我如何使用 python-requests 做到这一点?事件挂钩是正确的工具还是我需要不同的东西? (注意:由于各种原因,我不能选择 Scrapy。)非常感谢!

【问题讨论】:

    标签: python callback web-scraping python-requests


    【解决方案1】:

    我会这样做:

    import grequests
    from bs4 import BeautifulSoup
    
    
    def get_urls_from_response(r):
        soup = BeautifulSoup(r.text)
        urls = [link.get('href') for link in soup.find_all('a')]
        return urls
    
    
    def print_url(args):
        print args['url']
    
    
    def recursive_urls(urls):
        """
        Given a list of starting urls, recursively finds all descendant urls
        recursively
        """
        if len(urls) == 0:
            return
        rs = [grequests.get(url, hooks=dict(args=print_url)) for url in urls]
        responses = grequests.map(rs)
        url_lists = [get_urls_from_response(response) for response in responses]
        urls = sum(url_lists, [])  # flatten list of lists into a list
        recursive_urls(urls)
    

    我还没有测试过代码,但大致的想法就在那里。

    请注意,我使用 grequests 而不是 requests 来提高性能。 grequest 基本上是 gevent+request,根据我的经验,由于您检索与 gevent 异步的链接,这种任务的速度要快得多


    编辑:这是相同的算法,但不使用递归:

    import grequests
    from bs4 import BeautifulSoup
    
    
    def get_urls_from_response(r):
        soup = BeautifulSoup(r.text)
        urls = [link.get('href') for link in soup.find_all('a')]
        return urls
    
    
    def print_url(args):
        print args['url']
    
    
    def recursive_urls(urls):
        """
        Given a list of starting urls, recursively finds all descendant urls
        recursively
        """
        while True:
            if len(urls) == 0:
                break
            rs = [grequests.get(url, hooks=dict(args=print_url)) for url in urls]
            responses = grequests.map(rs)
            url_lists = [get_urls_from_response(response) for response in responses]
            urls = sum(url_lists, [])  # flatten list of lists into a list
    
    if __name__ == "__main__":
        recursive_urls(["INITIAL_URLS"])
    

    【讨论】:

    • 非常感谢您的详细回答。如果我错了,请纠正我,但您在这里没有使用回调,而只是一个简单的递归函数调用。当达到一定的递归深度时,这不会导致问题吗?据我所知,Python 尚未针对递归进行优化,因此您的设置可能会在某些时候抛出 RuntimeError 还是我错了?
    • @PeterStahl 你是对的,它最多只会递归sys.getrecursionlimit(),在我的盒子里是 1000。如果需要,我会尝试提出一个没有此限制的答案。
    • 确实不错,因为这是我遇到的主要问题。我想我需要并行编程,但我没有这方面的经验。如果您想出一些东西会很好,因为我真的不知道从哪里开始(使用线程或进程,外部库,如 Celery 等)。提前致谢。
    • @PeterStahl 没问题,我在不使用递归的情况下添加了相同的代码。我还没有深入研究如何使用 grequests 的工作人员,但我想这是一个不同的问题。
    • 非常感谢。 :) OMG,一个while循环。确实如此简单。我本可以自己想出来的。哦,亲爱的,我还有很多东西要学。但这是一个很好的开始,也是我想知道的。 :)
    猜你喜欢
    • 2015-06-19
    • 1970-01-01
    • 1970-01-01
    • 2013-04-10
    • 2011-06-07
    • 1970-01-01
    • 2011-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多