CrawlSpider源码分析

CrawlSpider是对Spider做了进一步的封装, 使得该类可以直接爬取一个网站.也就是说CrawlSpider是为了爬取整个网站设计的

CrawlSpider和Spider一样, 入口都是start_request, 如果想要模拟登陆, 可以重载这个函数, 然后callback自己定义的登陆函数, 

关于模拟登陆可以参考之前写的 模拟登陆知乎 

CrawlSpider不同于Spider的是,:

1. 不能重载parse函数, 因为CrawlSpider对parse函数做了重载, 实现了相关的逻辑, 所以不能重载,

相应的可以重载 parse_start_url这个函数.

2. 多出来了一个rules , rules中指明了你要对这个网站的那些网页进行爬取, 那些网页不用进行爬取, 通过正则表达式匹配成功的才会做进一步的处理

有关rules下面会做进一步的说明, rules是如何起作用的

下面来解析CrawlSpider的源码

首先是parse函数:

    def parse(self, response):
        return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)

从代码中也能看到, 其中调用了CrawlSpider自己定义的函数 _parse_response, 这也是不能被重载的原因

下面是_parse_response函数:

    def _parse_response(self, response, callback, cb_kwargs, follow=True):
        if callback:
            cb_res = callback(response, **cb_kwargs) or ()
            cb_res = self.process_results(response, cb_res)
            for requests_or_item in iterate_spider_output(cb_res):
                yield requests_or_item

        if follow and self._follow_links:
            for request_or_item in self._requests_to_follow(response):
                yield request_or_item

其中的参数callback就是 parse_start_url, 就和Spider中的parse函数功能一样, 

如果没有写parse_start_url和process_result, 那么第一个if就没什么用, 当然我们也可以自己重载写上自己的定义, parse_start_url

返回的结果交给process_result进行处理, process_result返回的结果进行迭代, 然后yield出去

第二个if之最重要的一点:

当follow和self._follow_links其中有一个为False时, 都不会使rules生效.也就是说if后面的代码说明了rules是怎么生效的

现在跟踪_requests_to_follow函数:

     def _requests_to_follow(self, response):
        if not isinstance(response, HtmlResponse):
            return
        seen = set()
        for n, rule in enumerate(self._rules):
            links = [lnk for lnk in rule.link_extractor.extract_links(response)
                     if lnk not in seen]
            if links and rule.process_links:
                links = rule.process_links(links)
            for link in links:
                seen.add(link)
                r = self._build_request(n, link)
                yield rule.process_request(r)

首先判断response是否是网页的访问, 不是的话, 就直接结束

seen集合是为了去除该网页中相同的网页

for语句是将self._rules变为可迭代对象,

现在跟踪self._rules, 而该成员变量在_compile_rules中定义, 而compile_rules是在CrawlSpider初始化的时候被调用

compile_rules:

def _compile_rules(self):
    def get_method(method):
        if callable(method):
            return method
        elif isinstance(method, six.string_types):
            return getattr(self, method, None)

    self._rules = [copy.copy(r) for r in self.rules]
    for rule in self._rules:
        rule.callback = get_method(rule.callback)
        rule.process_links = get_method(rule.process_links)
        rule.process_request = get_method(rule.process_request)

首先是对rules进行浅拷贝,然后对rules中的每个rule进行处理, 其中callback就是rule中自己指明的要调用的函数, 另外两个函数不做分析

现在回到_requests_to_follow

for语句之后的代码就是根据rule中的规则提取出网页中的所有链接, 然后将所得链接生成request, 之后调用_parse_response函数, 

进行进一步的爬取, 直到将网站中的所有网页爬取完毕

当爬取的网站需要user_agent时, 可以修改_build_request函数, 直接添加headers即可

原文地址:https://www.cnblogs.com/fenglj/p/7910089.html