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即可