?注意:可配置爬虫仅Python版本(v0.2.1-v0.2.4)可用,最新版本暂不可用Golang版本(v0.3.0),后续会添加,请关注近期更新后台在实际的大型爬虫开发项目中,需要爬虫工程师爬取和监控几十个甚至上百个网站。总的来说,这些网站的结构大同小异,主要区别在于抓取项目的提取规则。传统的做法是让爬虫工程师写一个通用的框架,然后把每个网站的抽取规则做成可配置的,然后把配置工作交给更多的初级工程师或者外包。这样,简化了爬虫的开发,提高了一些生产效率。但是,配置的工作仍然是一项艰巨的工作,仍然需要消耗大量的人力。因此,自动提取字段应运而生。字段自动提取是Crawlabv0.2.2版本基于可配置爬虫开发的新特性。允许用户自动提取可能需要爬取的列表项,无需任何繁琐的提取规则配置,实现真正的“一键爬取”。如果顺利的话,半分钟就可以完成一个网站爬虫的开发。市面上有使用机器学习自动抓取要提取的抓取规则的方法,有的可以准确提取,可惜平台收取高额费用,个人开发者或者小公司一般无法承受。Crawlab自动提取字段是根据人的抓取模式模拟的,无需培训即可使用。此外,Crawlab不会为其自动字段提取功能向用户收费,因为Crawlab本身是免费的。算法介绍算法的核心来自于人类行为本身,通过在网页中寻找类似列表的元素来定位列表并抓取项目。一般我们查找列表项的流程是怎样的?有人说:这还不简单吗?一眼就能看出这是一个列表!大哥,拜托。。。我们是从程序的角度说的,它只懂HTML、CSS、JS代码,它没有你聪明。当我们识别一个列表时,我们首先需要看它是否有很多相似的子项;其次,这些列表通常看起来“复杂”并且包含许多可见元素;最后,我们还需要注意分页,分页按钮一般称为“NextPage”、“NextPage”、“Next”、“NextPage”等。用程序可以理解的语言,我们将上面的规则概括为:列表项从根节点开始,从上到下遍历标签;对于每个标签,如果包含多个相同的子标签,则判断为列表标签候选;递归)编号最大的列表标签候选为列表标签;listsubitem是通过以上规则抽取的list标签,并(递归)遍历每个子标签,在target字段中添加带href的a标签;带有文本的标签将加入目标字段。Pagination对于每个标签,如果标签文本是特定文本(“下一页”、“下一页”、“下一页”、“下一页”),则选择该标签作为目标标签。这样,我们就设计了自动提取列表项、列表子项和分页的规则。剩下的就是写代码了。我知道这样的设计太简单太理想了,没有考虑到一些特殊情况。稍后我们将通过在一些知名网站上对其进行测试来了解我们的算法的性能。算法实现算法实现非常简单。为了更好的操作HTML标签,我们选择了lxml库作为HTML操作库。lxml是一个python解析库,支持HTML和XML解析,支持XPath和CSS解析方式,解析效率非常高。自上而下的遍历语法是sel.iter()。sel为etree.Element,iter从根节点开始,从上到下遍历每个元素,直到遍历完所有元素。它是一个发电机。构造解析树获取到页面的HTML后,我们需要调用lxml中的etree.HTML方法来构造解析树。代码很简单如下,其中r是requests.get的Response#gethtmlparsetreesel=etree.HTML(r.content)这段代码在SpiderApi._get_html方法中。请在此处查看源代码。辅助函数在我们开始构建算法之前,我们需要实现一些辅助函数。所有功能都封装在SpiderApi类中,所以写法和类方法一样。@staticmethoddef_get_children(sel):#获取所有不包含注释的子节点递归子节点(根节点)tags=[]fortaginsel.iter():iftype(tag)!=etree._Commentandtag.textisnotNoneandtag.text.strip()!='':tags.append(tag)returntags@staticmethoddef_get_a_child_tags(sel):#递归获取所有超链接子节点(根节点)tags=[]fortaginsel.iter():iftag.tag=='a':iftag.get('href')不是None也不是tag.get('href').startswith('#')而不是tag.get('href').startswith('javascript'):tags.append(tag)返回标签获取列表项下面是核心中的核心!请同学们注意。让我们编写获取列表项的代码。下面是获取列表标签候选列表list_tag_list的代码。看起来有点复杂,其实逻辑很简单:对于每个节点,我们获取所有子节点(一级),过滤掉阈值以上的节点(默认10个),然后过滤掉子标签类别唯一的节点.这样就得到了候选列表。list_tag_list=[]threshold=spider.get('item_threshold')or10#自上而下迭代所有子节点fortaginsel.iter():#getchildtagschild_tags=self._get_children(tag)iflen(child_tags)1:continue#addaslisttaglist_tag_list.append(tag)接下来我们从候选列表中过滤掉包含最多文本子节点的节点。听起来有点啰嗦,打个比方:电商网站的listitem,也就是productitem,肯定有很多信息,比如价格,商品名称,卖家等等,所以它将包含很多文本节点。我们就是这样过滤掉文本信息很少的列表(比如菜单列表、分类列表等),得到最终的列表。在代码中,我们将其存储为max_tag。#找到子文本标签最多的列表标签max_tag=Nonemax_num=0fortaginlist_tag_list:_child_text_tags=self._get_text_child_tags(self._get_children(tag)[0])iflen(_child_text_tags)>max_num:max_tag=tagmax_num=len(_child_text_tags),我们将为列表项生成CSS选择器。下面代码实现的逻辑主要是根据上面获取到的target标签,根据其id或者class属性,生成一个CSS选择器。#获取列表项选择器item_selector=Noneifmax_tag.get('id')isnotNone:item_selector=f'#{max_tag.get("id")}>{self._get_children(max_tag)[0].tag}'elifmax_tag.get('class')不是None:cls_str='.'.join([xforxinmax_tag.get("class").split('')ifx!=''])iflen(sel.cssselect(f'.{cls_str}'))==1:item_selector=f'.{cls_str}>{self._get_children(max_tag)[0].tag}'找到目标列表项后,我们需要做的是提取其下方的文本标签和超链接标签。代码如下,不再赘述。有兴趣的读者可以看看源码了解一下。#获取列表字段fields=[]ifitem_selectorisnotNone:first_tag=self._get_children(max_tag)[0]fori,taginenumerate(self._get_text_child_tags(first_tag)):iflen(first_tag.cs??sselect(f'{tag).tag}'))==1:fields.append({'name':f'field{i+1}','type':'css','extract_type':'text','query':f'{tag.tag}',})eliftag.get('class')不是None:cls_str='.'.join([xforxintag.get("class").split('')如果x!=''])如果len(tag.cs??sselect(f'{tag.tag}.{cls_str}'))==1:fields.append({'name':f'field{i+1}','type':'css','extract_type':'text','query':f'{tag.tag}.{cls_str}',})fori,taginenumerate(self._get_a_child_tags(self._get_children(max_tag)[0])):#如果标签是,提取titshrefiftag.get('class')isnotNone:cls_str='.'.join([xforxintag.get("class").split('')ifx!=''])fields.append({'name':f'field{i+1}_url','type':'css','extract_type':'attribute','attribute':'href','query':f'{tag.tag}.{cls_str}',})分页的代码很简单,很容易实现,就不多说了。有兴趣的可以看源码,这样我们就可以提取列表项和列表子项要使用自动提取字段的算法,必须先安装Crawlab。安装方法请查看Github。Crawlab安装并运行后,您必须创建一个可配置的爬虫。详细步骤可以参考【【爬虫笔记】我是如何3分钟开发一个爬虫的】(https://juejin.im/post/5ceb43....创建完成后,我们来到配置选项卡创建的可配置爬虫的爬虫详情,输入起始URL,点击Extractfield按钮,Crawlab会从起始URL中提取列表字段,接下来点击Preview可以看到这些字段是否是有效字段,可以添加,删除,适当修改,如果是,点击运行,爬虫就会开始爬取数据,好了,你只需要这几步,剩下的就交给Crawlab吧,测试结果在这article测试了前10个电商网站,只有3个网站无法识别(因为“动态内容”、“列表没有id/class”、“lxml定位元素问题”),成功率是70%.读者可以尝试使用Crawlab的自动字段提取功能测试一下自己感兴趣的网站看看是否符合预期。结果的详细列表如下。网站淘宝N动态内容提取成功原因京东Y阿里巴巴1688Y搜网Y苏宁Y糯米Y麦购N列表没有id/class天猫Y当当网Nlxml定位元素问题Crawlab的算法当然,仍然需要改进,例如在考虑动态内容和没有锚点(如id/class)的列表时。也欢迎大家前来试用,甚至为项目贡献力量。Github:tikazyq/crawlab如果您觉得Crawlab对您的日常开发或者公司有帮助,请加作者微信加入开发交流群,大家一起交流Crawlab的使用和开发。