
scrapy分页抓取网页
scrapy分页分页抓取网页过程中的生命周期有哪些?
网站优化 • 优采云 发表了文章 • 0 个评论 • 76 次浏览 • 2022-08-31 18:00
scrapy分页抓取网页过程中的生命周期有哪些?查看下面handlers.py文件定义的scrapy任务循环时间片:pythonconfig.py定义各个方法,参数自己定义,可以是config,自己写的,也可以用scrapy官方提供的定义的,如果自己编写,一定要弄懂下面几个函数中的参数含义future*:执行该scrapy任务循环时的阻塞方法(如果阻塞则保持阻塞,否则会开始。
比如说一个循环循环500个timeout,如果当前是阻塞的状态,则会一直执行500-500-500-500-500这样循环500次)if-else:代表循环结束条件(所有的所有的方法会自动初始化好并且开始工作,如果出现其他条件阻塞,则阻塞程序中的每一行代码,并继续处理剩余的500行代码,如果是某一个方法自动初始化好并且处理了其他条件,则代表执行该方法中所有的语句,如果出现其他函数是阻塞的,则自动执行代码,处理剩余的500行代码)define:选择执行spider到一个关于抓取对象的类中spider=scrapy.spider(fields=('title','score','vote_num','capture_time','author','date','summary','min_delta','num_stories','max_stories','request_type','post_type','source','post','vote_mails','posts','text','binary','follow_tom','lookup','update','userid','user_id','email','qq','group','email_code','update_fields','distributor','custom_code','user_code','userid','user_pass','user_pass','custom_profile','posttext','user_pass','user_name','user_profile','author','start_time','end_time','fail_time','repost_time','spider','form','tag','message','action','timeout','create_request','response','post','encoding','timeout','post_long','post_long','response_source','request_source','trigger','spider_method','directory','gettick','trailing_timeout','data_item','default_layout','round','rounded','post_timeout','local_timeout','author_last_trigger','post_timeout','author_last_trigger','vote_num','capture_time','vote_num','vote_delta','summary','。 查看全部
scrapy分页分页抓取网页过程中的生命周期有哪些?

scrapy分页抓取网页过程中的生命周期有哪些?查看下面handlers.py文件定义的scrapy任务循环时间片:pythonconfig.py定义各个方法,参数自己定义,可以是config,自己写的,也可以用scrapy官方提供的定义的,如果自己编写,一定要弄懂下面几个函数中的参数含义future*:执行该scrapy任务循环时的阻塞方法(如果阻塞则保持阻塞,否则会开始。

比如说一个循环循环500个timeout,如果当前是阻塞的状态,则会一直执行500-500-500-500-500这样循环500次)if-else:代表循环结束条件(所有的所有的方法会自动初始化好并且开始工作,如果出现其他条件阻塞,则阻塞程序中的每一行代码,并继续处理剩余的500行代码,如果是某一个方法自动初始化好并且处理了其他条件,则代表执行该方法中所有的语句,如果出现其他函数是阻塞的,则自动执行代码,处理剩余的500行代码)define:选择执行spider到一个关于抓取对象的类中spider=scrapy.spider(fields=('title','score','vote_num','capture_time','author','date','summary','min_delta','num_stories','max_stories','request_type','post_type','source','post','vote_mails','posts','text','binary','follow_tom','lookup','update','userid','user_id','email','qq','group','email_code','update_fields','distributor','custom_code','user_code','userid','user_pass','user_pass','custom_profile','posttext','user_pass','user_name','user_profile','author','start_time','end_time','fail_time','repost_time','spider','form','tag','message','action','timeout','create_request','response','post','encoding','timeout','post_long','post_long','response_source','request_source','trigger','spider_method','directory','gettick','trailing_timeout','data_item','default_layout','round','rounded','post_timeout','local_timeout','author_last_trigger','post_timeout','author_last_trigger','vote_num','capture_time','vote_num','vote_delta','summary','。
scrapy分页抓取网页实战-11celery执行过程-陈愚依
网站优化 • 优采云 发表了文章 • 0 个评论 • 51 次浏览 • 2022-08-24 17:10
scrapy分页抓取网页实战-scrapyv1。11celery执行过程-陈愚依@棱镜scrapy深度学习抓取youtube热门视频-room来自捷克的html5前端框架d3。js读取电商网站的js文件,css文件一个root权限,也要看你是否登录不会的请问我,我每天做这个重复工作好多,求大神指导。
之前用scrapy在聚合类网站扒了很多html。
研究了一下百度文库的问答机制,可能是因为你的doc和res主题的关系,
sqlite不支持ajax请求,有时候请求相关的信息可能需要跳转,你可以添加pythonsite对象来实现跳转,就是类似调用对应dom元素来调用ajax请求(不同dom元素调用方法一样)。ajax之前还支持过轮询:djangositejsperfmcom目前github的sitemap是不支持轮询的,你可以做一个类似于百度+py文库的网页,每当显示完相关文件需要记录一下就跳转。
pythoncelery抓取scrapy文件
试过scrapy,
微软安装的python版本,python2,python3的执行环境不一样,用的java环境都不一样。 查看全部
scrapy分页抓取网页实战-11celery执行过程-陈愚依
scrapy分页抓取网页实战-scrapyv1。11celery执行过程-陈愚依@棱镜scrapy深度学习抓取youtube热门视频-room来自捷克的html5前端框架d3。js读取电商网站的js文件,css文件一个root权限,也要看你是否登录不会的请问我,我每天做这个重复工作好多,求大神指导。
之前用scrapy在聚合类网站扒了很多html。

研究了一下百度文库的问答机制,可能是因为你的doc和res主题的关系,
sqlite不支持ajax请求,有时候请求相关的信息可能需要跳转,你可以添加pythonsite对象来实现跳转,就是类似调用对应dom元素来调用ajax请求(不同dom元素调用方法一样)。ajax之前还支持过轮询:djangositejsperfmcom目前github的sitemap是不支持轮询的,你可以做一个类似于百度+py文库的网页,每当显示完相关文件需要记录一下就跳转。

pythoncelery抓取scrapy文件
试过scrapy,
微软安装的python版本,python2,python3的执行环境不一样,用的java环境都不一样。
scrapy.7.1官方文档:基于python2的异步框架,1.x的写法和1
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-07-27 16:04
scrapy分页抓取网页原理是找到页码所在的行,然后继续往下抓取就好了关于代码规范请看scrapy1.7.1官方文档
我一直认为scrapy是基于python2的异步框架,1.x的写法和1.x非常不同,但是2和2之间的用法是一样的!同步和异步还是要看你需要抓取的页面,1.x时代往往数据都在session内部,这里这个是数据分析基本功,要以自己实际项目为基准了。2.x更多的使用异步,因为将数据传递给spider的时候可以异步地传递到wsgi服务,如果是io请求则正常处理,spider可以处理数据库,epoll或者其他的。
3.spider也可以做爬虫,这个倒是没见过有人写成middleware设计的,因为页面容易多次执行,甚至不同的项目可能数据结构也不一样,是很难拿过来进行控制的,一般会找个开源框架来控制,或者参考如pyspider/zopextend·github等。
1.可以分页这一块我认为和hadoop类似2.如果java或者python语言基础也没什么问题的话,我推荐3.x版本,
因为看到这个问题知乎上很多相关回答,就回答一下吧。分页抓取其实就是根据重定向来抓取网页数据,一般实现原理都差不多,主要分为页面重定向和cookie重定向,下面以httpclient为例子解释一下。分页的抓取其实原理不复杂,只要实现了request模块的cookie重定向,并且指定了参数即可。根据我接触到的分页抓取来看,重定向一般分为两种:连续的提交请求和单次请求下面分别介绍下两种方式1.连续的提交请求重定向方式a.在客户端实现重定向,连续提交多个请求。
例如:每次请求之间都存在关联性,这样抓取效率高,后端会对每个请求发送多个数据包,合并数据包即可抓取多个数据包。b.单次的请求如果被定义一个等间隔(默认3毫秒)请求,那么抓取会在不同时间点继续向后端返回相同请求。具体请求的返回结果表中会详细列出以返回的数据包中每个包的返回状态,对应不同的返回信息,同时返回或者在后端返回同一页抓取方式的请求中,请求方式会在request.urls中列出返回的地址和response.statuscode值。
2.单次的请求后端发送单次request,a.需要python的selector类,它包含最适合某个请求的选择器,例如book,一个状态字符串就会得到最适合的book.book.headers#请求的状态字符串withopen(file.text,'w')asf:book=pd.read_html(selector.headers)#post请求的状态字符串,返回是post方式book.encoding='utf-8'#error输出输入的error值#lambda函数headers这里说明一。 查看全部
scrapy.7.1官方文档:基于python2的异步框架,1.x的写法和1
scrapy分页抓取网页原理是找到页码所在的行,然后继续往下抓取就好了关于代码规范请看scrapy1.7.1官方文档
我一直认为scrapy是基于python2的异步框架,1.x的写法和1.x非常不同,但是2和2之间的用法是一样的!同步和异步还是要看你需要抓取的页面,1.x时代往往数据都在session内部,这里这个是数据分析基本功,要以自己实际项目为基准了。2.x更多的使用异步,因为将数据传递给spider的时候可以异步地传递到wsgi服务,如果是io请求则正常处理,spider可以处理数据库,epoll或者其他的。

3.spider也可以做爬虫,这个倒是没见过有人写成middleware设计的,因为页面容易多次执行,甚至不同的项目可能数据结构也不一样,是很难拿过来进行控制的,一般会找个开源框架来控制,或者参考如pyspider/zopextend·github等。
1.可以分页这一块我认为和hadoop类似2.如果java或者python语言基础也没什么问题的话,我推荐3.x版本,

因为看到这个问题知乎上很多相关回答,就回答一下吧。分页抓取其实就是根据重定向来抓取网页数据,一般实现原理都差不多,主要分为页面重定向和cookie重定向,下面以httpclient为例子解释一下。分页的抓取其实原理不复杂,只要实现了request模块的cookie重定向,并且指定了参数即可。根据我接触到的分页抓取来看,重定向一般分为两种:连续的提交请求和单次请求下面分别介绍下两种方式1.连续的提交请求重定向方式a.在客户端实现重定向,连续提交多个请求。
例如:每次请求之间都存在关联性,这样抓取效率高,后端会对每个请求发送多个数据包,合并数据包即可抓取多个数据包。b.单次的请求如果被定义一个等间隔(默认3毫秒)请求,那么抓取会在不同时间点继续向后端返回相同请求。具体请求的返回结果表中会详细列出以返回的数据包中每个包的返回状态,对应不同的返回信息,同时返回或者在后端返回同一页抓取方式的请求中,请求方式会在request.urls中列出返回的地址和response.statuscode值。
2.单次的请求后端发送单次request,a.需要python的selector类,它包含最适合某个请求的选择器,例如book,一个状态字符串就会得到最适合的book.book.headers#请求的状态字符串withopen(file.text,'w')asf:book=pd.read_html(selector.headers)#post请求的状态字符串,返回是post方式book.encoding='utf-8'#error输出输入的error值#lambda函数headers这里说明一。
scrapy分页抓取网页的相关细节介绍可以参考中的文档和社区joshuaseng
网站优化 • 优采云 发表了文章 • 0 个评论 • 61 次浏览 • 2022-07-26 11:01
scrapy分页抓取网页的相关细节介绍可以参考scrapy中的文档和社区joshuaseng:scrapy分页抓取网页的相关细节介绍如果你理解executor的执行流程的话,在scrapy中的pythonexecutor、httpexecutor、gevent的执行流程都一样,都是将参数包装为相应的streams,并不断分包,分好后在内存中进行拷贝。
只是本质上是scrapy的作用是解决异步抓取,抓取更多网页时用gevent更多,那这里介绍的executor虽然是抓取同一个网页,但它也是一个异步任务队列的实现,主要用来解决每个抓取的streams有不同的队列,这种blockingstreamscheduler模型不好处理分页抓取,为了更好的处理分页抓取任务,scrapy提供了自定义的异步任务队列catchtasks,executor可以直接用它来执行异步任务,只是有时会对异步任务的处理结果产生一些影响,很多人都知道在抓取完成后回退时executor将会处理分页任务,这对于处理分页相关请求会带来影响,因为分页是重复抓取过程,在收到重复信息时executor会返回一个redisobject.structure(name="",root=0)来返回相应的字典。
所以我们会出现redisobject.structure("'',ip='10.99.139.137',ds=[''])这种格式的处理结果并不安全,而且会报错。executor的源码在scrapy/executor/python.py中。但在python2.x中,在抓取过程中gevent协程是跟executor一起执行的,在2.x中,scrapy使用scrapyqtcore替代了gevent,它是scrapy协程管理进程的基本框架,不同gevent协程和scrapyqtcore绑定在不同的客户端上,executor是pythonpython代码代理的实现,每个gevent协程可以有多个实例,在实际中可以使用--gevent属性来指定所有gevent协程实例的线程数量,然后在qtcore执行本地任务来主动调用实例来执行一系列本地任务,使用“cpus”给cpu个数,cpu的每一个时钟周期就执行一个本地任务。
这样qtcore中全都是executor实例而不是gevent协程,这样做的好处是scrapy1.6版本引入了mutable_scheduler,也就是scrapy自带异步分页机制,从而不需要手动启动gevent协程。本文以分页抓取一个chinaz网站下的某个热门旅游城市的热门人物的个人的信息,这些信息会以文本列表的形式展示给我们,点击会跳转到该个人的主页,在本文中,我只抓取旅游城市的热门人物的个人信息。
fromscrapy.requestimporturlfromscrapy.cookieimportkeywordsfromscrapy.exceptionsimportexcusefroms。 查看全部
scrapy分页抓取网页的相关细节介绍可以参考中的文档和社区joshuaseng
scrapy分页抓取网页的相关细节介绍可以参考scrapy中的文档和社区joshuaseng:scrapy分页抓取网页的相关细节介绍如果你理解executor的执行流程的话,在scrapy中的pythonexecutor、httpexecutor、gevent的执行流程都一样,都是将参数包装为相应的streams,并不断分包,分好后在内存中进行拷贝。

只是本质上是scrapy的作用是解决异步抓取,抓取更多网页时用gevent更多,那这里介绍的executor虽然是抓取同一个网页,但它也是一个异步任务队列的实现,主要用来解决每个抓取的streams有不同的队列,这种blockingstreamscheduler模型不好处理分页抓取,为了更好的处理分页抓取任务,scrapy提供了自定义的异步任务队列catchtasks,executor可以直接用它来执行异步任务,只是有时会对异步任务的处理结果产生一些影响,很多人都知道在抓取完成后回退时executor将会处理分页任务,这对于处理分页相关请求会带来影响,因为分页是重复抓取过程,在收到重复信息时executor会返回一个redisobject.structure(name="",root=0)来返回相应的字典。
所以我们会出现redisobject.structure("'',ip='10.99.139.137',ds=[''])这种格式的处理结果并不安全,而且会报错。executor的源码在scrapy/executor/python.py中。但在python2.x中,在抓取过程中gevent协程是跟executor一起执行的,在2.x中,scrapy使用scrapyqtcore替代了gevent,它是scrapy协程管理进程的基本框架,不同gevent协程和scrapyqtcore绑定在不同的客户端上,executor是pythonpython代码代理的实现,每个gevent协程可以有多个实例,在实际中可以使用--gevent属性来指定所有gevent协程实例的线程数量,然后在qtcore执行本地任务来主动调用实例来执行一系列本地任务,使用“cpus”给cpu个数,cpu的每一个时钟周期就执行一个本地任务。

这样qtcore中全都是executor实例而不是gevent协程,这样做的好处是scrapy1.6版本引入了mutable_scheduler,也就是scrapy自带异步分页机制,从而不需要手动启动gevent协程。本文以分页抓取一个chinaz网站下的某个热门旅游城市的热门人物的个人的信息,这些信息会以文本列表的形式展示给我们,点击会跳转到该个人的主页,在本文中,我只抓取旅游城市的热门人物的个人信息。
fromscrapy.requestimporturlfromscrapy.cookieimportkeywordsfromscrapy.exceptionsimportexcusefroms。
多逛逛,看看。我也只能从文档里学到的
网站优化 • 优采云 发表了文章 • 0 个评论 • 104 次浏览 • 2022-07-20 17:04
scrapy分页抓取网页:scrapy1。1。0中文文档网页浏览器分页:scrapy1。1。0中文文档scrapy-python文档|scrapy中文文档scrapy-siteapp文档|scrapy中文文档scrapy-crawler文档|scrapy中文文档scrapy-python中文文档scrapy-startpd爬虫框架中文文档:scrapy-siteapp中文文档scrapy-python中文文档。
scrapy0.3.0中文文档-scrapy中文文档和scrapy0.01版本-scrapy中文文档基于python的web服务器|scrapydocs
项目给出的docid
参考百度镜像站的csdn镜像站的scrapy教程,很全。我也是看别人的博客学到的。
可以直接用scrapy爬虫思想结合爬虫框架构建python爬虫框架scrapy
scrapy有中文文档
目前正在学习中,欢迎大家讨论。
能不能有个人手把手教学的,免费的,
《scrapy中文文档》发布在爬虫资源网上scrapy中文文档:
如果你把http_response想象成scrapy对应文档的html_response的话,这个问题就迎刃而解了。
多逛逛,看看。
我也只能从文档里学到点皮毛。毕竟这本书大半不是我写的。只是入门水平。
让我看一下scrapy的运行过程,再让我看看http的协议有多简单,再让我运行用java写的代码,最后看看scrapy部署。大概需要一个月。 查看全部
多逛逛,看看。我也只能从文档里学到的
scrapy分页抓取网页:scrapy1。1。0中文文档网页浏览器分页:scrapy1。1。0中文文档scrapy-python文档|scrapy中文文档scrapy-siteapp文档|scrapy中文文档scrapy-crawler文档|scrapy中文文档scrapy-python中文文档scrapy-startpd爬虫框架中文文档:scrapy-siteapp中文文档scrapy-python中文文档。
scrapy0.3.0中文文档-scrapy中文文档和scrapy0.01版本-scrapy中文文档基于python的web服务器|scrapydocs
项目给出的docid
参考百度镜像站的csdn镜像站的scrapy教程,很全。我也是看别人的博客学到的。

可以直接用scrapy爬虫思想结合爬虫框架构建python爬虫框架scrapy
scrapy有中文文档
目前正在学习中,欢迎大家讨论。
能不能有个人手把手教学的,免费的,

《scrapy中文文档》发布在爬虫资源网上scrapy中文文档:
如果你把http_response想象成scrapy对应文档的html_response的话,这个问题就迎刃而解了。
多逛逛,看看。
我也只能从文档里学到点皮毛。毕竟这本书大半不是我写的。只是入门水平。
让我看一下scrapy的运行过程,再让我看看http的协议有多简单,再让我运行用java写的代码,最后看看scrapy部署。大概需要一个月。
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 126 次浏览 • 2022-06-27 07:45
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。

使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 67 次浏览 • 2022-06-25 15:17
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置

Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果

V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页

V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 69 次浏览 • 2022-06-24 23:29
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 64 次浏览 • 2022-06-23 19:10
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 75 次浏览 • 2022-06-22 03:09
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 54 次浏览 • 2022-06-21 06:55
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 67 次浏览 • 2022-06-21 02:22
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-06-19 01:25
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
【零基础学爬虫】scrapy实战:爬取知乎【所有用户】信息
网站优化 • 优采云 发表了文章 • 0 个评论 • 52 次浏览 • 2022-06-18 05:55
简介
知乎用户信息是非常大的,本文是一个scrapy实战:怎样抓取所有知乎用户信息。
爬取的思路如下图所示:
站点分析
本文以轮子哥为根节点(根节点可以随便选择),打开轮子哥的关注列表,并翻页查看关注列表:
翻页是一个AJAX请求,每页20个关注用户,并且有一些简要的用户信息
其中有一个是url-token,它是用来标识一个用户的,在上面截图中那个用户的主页url为:
其中就有url-token
项目实战
创建项目
思路分析
获取用户信息
首先需要获取用户的基本信息,这个基本信息可以通过请求类似下面的url获得:
{url_token}/following
而url_token可以在用户的关注列表中获取,上面url的页面类似于这个
另外,还有右侧获得赞同、被收藏、感谢的次数
从上述url获取用户基本信息后,接下来就是获取当前用户的关注列表:
获取用户的关注列表
这个列表存在翻页,经过分析得知关注列表的翻页是通过AJAX请求实现的,获取某页关注列表url如下所示:
%5B%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%ics&offset=0&limit=20
提取一下规律:
{url-token}/followees?include={include}&offset={offset}&limit=2{limit}
其中include为:
data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics
上面url返回时一个json,该json中包含了20个关注者,response数据如下图所示:
对于一个关注者,我们只需要获取该用户的url-token,通过url-token即可拼接出该用户的主页,也就可以获得该用户的基本信息,以及关注者列表等。
获取到url-token后,我们需要判断有没有下一页,如果有则进行翻页,在上图的后半部分还有一个字段:
翻到当前的最后一页,该字段如下图所示:
因此依据is_end字段就可以判断是否下一页关注者了,使用next字段的值即可获取下一页关注者的列表了。
思路整理
所以呢?在解析用户基本信息的同时,我们可以拿到用户的url_token,进一步我们也就可以拿到该用户的关注者列表,如此递爬取,基本可获得所有知乎用户的所有基本信息。
本次的pipline设置为:
源码和爬取的部分数据:
爬取的部分知乎用户数据
源码
爬取知乎用户的思路已经在文章介绍了,思路理好后,实现比较块。
主要是源码有点长,这里就不全贴出来了。需要源码的同学可以通过文末方式获取
扫描下方二维码,公众号菜鸟名企梦后台发送关键词“知乎”即可获取本文的完整源码和详细程序注释
长按下面二维码,
发送“知乎”即可获取完整源码
热 文推 荐
公众号后台发送“爬虫”,即可获取“零基础学爬虫专栏”系列文章
公众号后台回复“目录”:即可获取小编熬夜编排整理好的历史文章:
目录截图如下(可上下滑动):
喜欢就点「在看」吧 ! 查看全部
【零基础学爬虫】scrapy实战:爬取知乎【所有用户】信息
简介
知乎用户信息是非常大的,本文是一个scrapy实战:怎样抓取所有知乎用户信息。
爬取的思路如下图所示:
站点分析
本文以轮子哥为根节点(根节点可以随便选择),打开轮子哥的关注列表,并翻页查看关注列表:
翻页是一个AJAX请求,每页20个关注用户,并且有一些简要的用户信息
其中有一个是url-token,它是用来标识一个用户的,在上面截图中那个用户的主页url为:
其中就有url-token
项目实战
创建项目
思路分析
获取用户信息
首先需要获取用户的基本信息,这个基本信息可以通过请求类似下面的url获得:
{url_token}/following
而url_token可以在用户的关注列表中获取,上面url的页面类似于这个
另外,还有右侧获得赞同、被收藏、感谢的次数
从上述url获取用户基本信息后,接下来就是获取当前用户的关注列表:
获取用户的关注列表
这个列表存在翻页,经过分析得知关注列表的翻页是通过AJAX请求实现的,获取某页关注列表url如下所示:
%5B%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%ics&offset=0&limit=20
提取一下规律:
{url-token}/followees?include={include}&offset={offset}&limit=2{limit}
其中include为:
data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics
上面url返回时一个json,该json中包含了20个关注者,response数据如下图所示:
对于一个关注者,我们只需要获取该用户的url-token,通过url-token即可拼接出该用户的主页,也就可以获得该用户的基本信息,以及关注者列表等。
获取到url-token后,我们需要判断有没有下一页,如果有则进行翻页,在上图的后半部分还有一个字段:
翻到当前的最后一页,该字段如下图所示:
因此依据is_end字段就可以判断是否下一页关注者了,使用next字段的值即可获取下一页关注者的列表了。
思路整理
所以呢?在解析用户基本信息的同时,我们可以拿到用户的url_token,进一步我们也就可以拿到该用户的关注者列表,如此递爬取,基本可获得所有知乎用户的所有基本信息。
本次的pipline设置为:
源码和爬取的部分数据:
爬取的部分知乎用户数据
源码
爬取知乎用户的思路已经在文章介绍了,思路理好后,实现比较块。
主要是源码有点长,这里就不全贴出来了。需要源码的同学可以通过文末方式获取
扫描下方二维码,公众号菜鸟名企梦后台发送关键词“知乎”即可获取本文的完整源码和详细程序注释
长按下面二维码,
发送“知乎”即可获取完整源码
热 文推 荐
公众号后台发送“爬虫”,即可获取“零基础学爬虫专栏”系列文章
公众号后台回复“目录”:即可获取小编熬夜编排整理好的历史文章:
目录截图如下(可上下滑动):


喜欢就点「在看」吧 !
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 80 次浏览 • 2022-06-18 05:54
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构
网站优化 • 优采云 发表了文章 • 0 个评论 • 61 次浏览 • 2022-05-22 23:00
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构——html文件通过网页抓取的正则表达式匹配其中一段然后根据规则解析出所需的网页代码,这种思路一定要牢记next表达式是分页分页的html文件中一定会出现一个名为next的变量代表下一页,next变量指向的网页代码也是分页的网页源码例如我们分页抓取所有urls.py为json格式的页面代码,那么next变量指向的html文件中页码为0当我们一页页的访问时,前面某个网页点击不断滚动就会抓取到下一页,在进行next判断,如果next表达式为真,即next变量为true,就表示抓取到最新一页,否则返回我们刚才网页查看源码会发现,将抓取到的所有网页都和之前抓取到的网页网址对比,就可以知道页码是从0开始的,这就是我们认为的的next判断true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页以前学习的网页爬虫分页抓取html文件的格式其实是list({'页数':'page','页码':'page','网址':'../basic/'})查看源码,一次仅抓取一个页面,例如我们抓取a.txt这个html文件,当我们访问b.txt时,b.txt返回的页码是空,此时判断页码是从0开始的。
我们想尽可能的不抓取b.txt中的网址,而是只抓取a.txt中的网址那该怎么判断页码是从0开始呢?想象一下当你解析b.txt中的网址的时候,如果页码也是从0开始,那你就需要再抓取一个关联的b.txt中的页码,然后把每一页的网址传入上述函数,这个过程就是分页true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页这里要注意next变量的形式,当变量长度为0时,next变量会随机覆盖每一页的网址,不存在使用next重复定时抓取的问题重点是判断在每次循环中,网页的item是否是新页面重新定时抓取网页,如果不是,那么next判断true值为false,你可以抛弃循环过程,直接返回完整的页码去除开头几行代码在匹配网页源码的时候,一定要注意先看list({'页码':'page','页码':'page','网址':'../basic/'})的数组形式(不要想当然的写url),然后判断里面每一个元素对应的页码是否从0开始的然后再把每个元素和上面的页码进行比较判断出是从0开始循环抓取(为什么?试一下就知道了)。
关于scrapy的变量化判断,这里没有写具体什么,比如env下的变量keys可以认为是和它对应的html源码上述的判断过程只是html源码的变量化简单的对角色内存地址的判断,根据n。 查看全部
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构——html文件通过网页抓取的正则表达式匹配其中一段然后根据规则解析出所需的网页代码,这种思路一定要牢记next表达式是分页分页的html文件中一定会出现一个名为next的变量代表下一页,next变量指向的网页代码也是分页的网页源码例如我们分页抓取所有urls.py为json格式的页面代码,那么next变量指向的html文件中页码为0当我们一页页的访问时,前面某个网页点击不断滚动就会抓取到下一页,在进行next判断,如果next表达式为真,即next变量为true,就表示抓取到最新一页,否则返回我们刚才网页查看源码会发现,将抓取到的所有网页都和之前抓取到的网页网址对比,就可以知道页码是从0开始的,这就是我们认为的的next判断true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页以前学习的网页爬虫分页抓取html文件的格式其实是list({'页数':'page','页码':'page','网址':'../basic/'})查看源码,一次仅抓取一个页面,例如我们抓取a.txt这个html文件,当我们访问b.txt时,b.txt返回的页码是空,此时判断页码是从0开始的。
我们想尽可能的不抓取b.txt中的网址,而是只抓取a.txt中的网址那该怎么判断页码是从0开始呢?想象一下当你解析b.txt中的网址的时候,如果页码也是从0开始,那你就需要再抓取一个关联的b.txt中的页码,然后把每一页的网址传入上述函数,这个过程就是分页true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页这里要注意next变量的形式,当变量长度为0时,next变量会随机覆盖每一页的网址,不存在使用next重复定时抓取的问题重点是判断在每次循环中,网页的item是否是新页面重新定时抓取网页,如果不是,那么next判断true值为false,你可以抛弃循环过程,直接返回完整的页码去除开头几行代码在匹配网页源码的时候,一定要注意先看list({'页码':'page','页码':'page','网址':'../basic/'})的数组形式(不要想当然的写url),然后判断里面每一个元素对应的页码是否从0开始的然后再把每个元素和上面的页码进行比较判断出是从0开始循环抓取(为什么?试一下就知道了)。
关于scrapy的变量化判断,这里没有写具体什么,比如env下的变量keys可以认为是和它对应的html源码上述的判断过程只是html源码的变量化简单的对角色内存地址的判断,根据n。
scrapy分页抓取网页视频还需要根据实际情况加上语句
网站优化 • 优采云 发表了文章 • 0 个评论 • 72 次浏览 • 2022-05-17 10:06
scrapy分页抓取网页视频很多时候我们都是需要分页的,比如我们要抓取某一页中的所有喜剧视频,对于分页抓取的时候,还需要对数据进行转换然后使用循环或其他方式。scrapy中的分页就是其中一种解决方案。大家都知道scrapy中不支持tabe情况,但也不可以一行代码完全处理分页,所以还需要加上taben形式的字符串和数据结构。
fromscrapy.contextimportcontextfromscrapy.collectionimportfilterfromscrapy.exceptionsimportexceptionclassmore_item(context):def__init__(self,item_path):"""definetheitempath.forexample:port=item_path,available_perpage=item_path,none=item_path...send_content=filter(item_path.select('.'),random=false)more_item=context(more_item=more_item)"""self.list_page=[item_path]self.exception=exception(more_item)上面代码大致框架就是这样,具体还需要根据实际情况加上语句。
header={'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','user-agent':'mozilla/5.0(windowsnt6.1;wow64)applewebkit/537.36(khtml,likegecko)chrome/67.0.3497.139safari/537.36'}content_length=len(self.res_data)defget_item(self,item_path):self.list_page=[]res=self.res_data[item_path].filter(item_path=item_path,random=true)defsend_content(self,sentence):print("sentence:",sentence)self.exception=exception(more_item=sentence)ifsentence.startswith(self.res_data.keywords(sentence.keyword_base)):item_dict=self.res_data[item_path].filter(content_dict=item_dict)item_list=list(item_dict)foriteminitem_dict:sub=item[item.keyword]print("port:",sub)print("available_perpage:",sub)print("none:",sub)else:print("0-5-4-6...")defget_tail_page(self,item_path):content_length=len(self.res_data)res=self.res_data[item_path].filter(content。 查看全部
scrapy分页抓取网页视频还需要根据实际情况加上语句
scrapy分页抓取网页视频很多时候我们都是需要分页的,比如我们要抓取某一页中的所有喜剧视频,对于分页抓取的时候,还需要对数据进行转换然后使用循环或其他方式。scrapy中的分页就是其中一种解决方案。大家都知道scrapy中不支持tabe情况,但也不可以一行代码完全处理分页,所以还需要加上taben形式的字符串和数据结构。
fromscrapy.contextimportcontextfromscrapy.collectionimportfilterfromscrapy.exceptionsimportexceptionclassmore_item(context):def__init__(self,item_path):"""definetheitempath.forexample:port=item_path,available_perpage=item_path,none=item_path...send_content=filter(item_path.select('.'),random=false)more_item=context(more_item=more_item)"""self.list_page=[item_path]self.exception=exception(more_item)上面代码大致框架就是这样,具体还需要根据实际情况加上语句。
header={'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','user-agent':'mozilla/5.0(windowsnt6.1;wow64)applewebkit/537.36(khtml,likegecko)chrome/67.0.3497.139safari/537.36'}content_length=len(self.res_data)defget_item(self,item_path):self.list_page=[]res=self.res_data[item_path].filter(item_path=item_path,random=true)defsend_content(self,sentence):print("sentence:",sentence)self.exception=exception(more_item=sentence)ifsentence.startswith(self.res_data.keywords(sentence.keyword_base)):item_dict=self.res_data[item_path].filter(content_dict=item_dict)item_list=list(item_dict)foriteminitem_dict:sub=item[item.keyword]print("port:",sub)print("available_perpage:",sub)print("none:",sub)else:print("0-5-4-6...")defget_tail_page(self,item_path):content_length=len(self.res_data)res=self.res_data[item_path].filter(content。
Python抓取公众号文章并生成pdf文件保存到本地
网站优化 • 优采云 发表了文章 • 0 个评论 • 66 次浏览 • 2022-05-10 01:24
前面一篇文章由于做的时间比较仓促还留下了几个问题:
分页的时候出现了数据重复,
什么时候爬取完了数据,根本不知道
那些文章是原创,那些文章非原创还没有标记
把公众号文章转存到本地. 方便以后阅读.
公众号文章转存到本地的效果图
友情提示: 所有的抓包操作,请用自己的微信小号来操作,我不知道官方会不会有封号操作,反正小心使得成年船!
分页的时候数据出现了重复
我发现这里返回的数据有一个next_offset这个字段,看意思就是想下一页的数据偏移量,于是我把数据每次取出来之后,重新赋值,发现数据果然没有再次重复了.
self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br />
什么时候爬取完了数据,根本不知道
我在Charles中不断抓取数据,发现在抓取到尾页的时候,发现 can_msg_continue 状态变为0了,于是有了这一行代码
if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br />
那些文章是原创,那些文章非原创还没有标记
很多时候我比较喜欢原创文章,也深深尊重这些原创工作者,他们的文章都花费了大量心血,公众号正好有原创标记,所以我猜应该有原创字段标识, 我通过Charles抓包分析, 发现 copyright_stat 这个字段与版权意思比较接近,然后再进一步分析,发现这个状态等于11的时候,是为原创
copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br />
完整的抓取数据代码就是这些:
def parse_data(self, response_data):<br /><br /> all_datas = json.loads(response_data)<br /><br /> if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br /> summy_datas = all_datas['general_msg_list']<br /> datas = json.loads(summy_datas)['list']<br /> for data in datas:<br /> try:<br /> title = data['app_msg_ext_info']['title']<br /> title_child = data['app_msg_ext_info']['digest']<br /> article_url = data['app_msg_ext_info']['content_url']<br /> cover = data['app_msg_ext_info']['cover']<br /> copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br /> self.count = self.count + 1<br /> print('第【{}】篇文章'.format(self.count), copyright, title, title_child, article_url, cover)<br /> self.creat_pdf_file(article_url, '{}_{}'.format(copyright, title))<br /> except:<br /> continue<br /><br /> time.sleep(3)<br /> self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br /> else:<br /> if 0 == all_datas['can_msg_continue']:<br /> exit('数据抓取完毕!')<br /> else:<br /> exit('数据抓取出错:' + all_datas['errmsg'])<br />
把公众号文章转存到本地. 方便以后阅读.
仅仅把文章抓取到完全不能满足我的兴趣,我想把有价值的公众号文章放到本地自己来查看一下,于是我就有了我把文章转成pdf 文档的想法,怎么把文章转成pdf文档呢,
分下面三步操作:
self.config = pdfkit.configuration(wkhtmltopdf='C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe') # 这里需要配置一下wkhtmlpdf.exe路径<br /><br /> def creat_pdf_file(self, url, title):<br /> try:<br /> file = 'D:/store/file/{}.pdf'.format(title)<br /> if not os.path.exists(file): # 过滤掉重复文件<br /> pdfkit.from_url(url, file, configuration=self.config)<br /><br /> except Exception as e:<br /> print(e)<br />
效果图如下:
缺点还是有的,网页中的图片无法写入在pdf文件中,另外这个pdfkit用法还有很多,这里我就不深入了,有兴趣的朋友可以自行深入!
文章源码完整代码,由于篇幅有限,请后台回复 公众号抓取文章源码
下一篇,我们去抓取公众号里面的文中 阅读量,点赞量,这对于判断一篇文章是否优质有重要参考意义,另外还要抓取评论,生成词云,我们看一篇热文有那些观点,请持续关注! 查看全部
Python抓取公众号文章并生成pdf文件保存到本地
前面一篇文章由于做的时间比较仓促还留下了几个问题:
分页的时候出现了数据重复,
什么时候爬取完了数据,根本不知道
那些文章是原创,那些文章非原创还没有标记
把公众号文章转存到本地. 方便以后阅读.
公众号文章转存到本地的效果图
友情提示: 所有的抓包操作,请用自己的微信小号来操作,我不知道官方会不会有封号操作,反正小心使得成年船!
分页的时候数据出现了重复
我发现这里返回的数据有一个next_offset这个字段,看意思就是想下一页的数据偏移量,于是我把数据每次取出来之后,重新赋值,发现数据果然没有再次重复了.
self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br />
什么时候爬取完了数据,根本不知道
我在Charles中不断抓取数据,发现在抓取到尾页的时候,发现 can_msg_continue 状态变为0了,于是有了这一行代码
if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br />
那些文章是原创,那些文章非原创还没有标记
很多时候我比较喜欢原创文章,也深深尊重这些原创工作者,他们的文章都花费了大量心血,公众号正好有原创标记,所以我猜应该有原创字段标识, 我通过Charles抓包分析, 发现 copyright_stat 这个字段与版权意思比较接近,然后再进一步分析,发现这个状态等于11的时候,是为原创
copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br />
完整的抓取数据代码就是这些:
def parse_data(self, response_data):<br /><br /> all_datas = json.loads(response_data)<br /><br /> if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br /> summy_datas = all_datas['general_msg_list']<br /> datas = json.loads(summy_datas)['list']<br /> for data in datas:<br /> try:<br /> title = data['app_msg_ext_info']['title']<br /> title_child = data['app_msg_ext_info']['digest']<br /> article_url = data['app_msg_ext_info']['content_url']<br /> cover = data['app_msg_ext_info']['cover']<br /> copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br /> self.count = self.count + 1<br /> print('第【{}】篇文章'.format(self.count), copyright, title, title_child, article_url, cover)<br /> self.creat_pdf_file(article_url, '{}_{}'.format(copyright, title))<br /> except:<br /> continue<br /><br /> time.sleep(3)<br /> self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br /> else:<br /> if 0 == all_datas['can_msg_continue']:<br /> exit('数据抓取完毕!')<br /> else:<br /> exit('数据抓取出错:' + all_datas['errmsg'])<br />
把公众号文章转存到本地. 方便以后阅读.
仅仅把文章抓取到完全不能满足我的兴趣,我想把有价值的公众号文章放到本地自己来查看一下,于是我就有了我把文章转成pdf 文档的想法,怎么把文章转成pdf文档呢,
分下面三步操作:
self.config = pdfkit.configuration(wkhtmltopdf='C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe') # 这里需要配置一下wkhtmlpdf.exe路径<br /><br /> def creat_pdf_file(self, url, title):<br /> try:<br /> file = 'D:/store/file/{}.pdf'.format(title)<br /> if not os.path.exists(file): # 过滤掉重复文件<br /> pdfkit.from_url(url, file, configuration=self.config)<br /><br /> except Exception as e:<br /> print(e)<br />
效果图如下:
缺点还是有的,网页中的图片无法写入在pdf文件中,另外这个pdfkit用法还有很多,这里我就不深入了,有兴趣的朋友可以自行深入!
文章源码完整代码,由于篇幅有限,请后台回复 公众号抓取文章源码
下一篇,我们去抓取公众号里面的文中 阅读量,点赞量,这对于判断一篇文章是否优质有重要参考意义,另外还要抓取评论,生成词云,我们看一篇热文有那些观点,请持续关注!
scrapy分页抓取网页 6000 多款 App,看我如何搞定她们并将其洗白白~
网站优化 • 优采云 发表了文章 • 0 个评论 • 56 次浏览 • 2022-05-09 21:33
项目文件创建好以后,我们就可以开始写爬虫程序了。
首先,需要在 items.py 文件中,预先定义好要爬取的字段信息名称,如下所示:
1class KuanItem(scrapy.Item):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2# define the fields for your item here like:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3name = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4volume = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5download = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6follow = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7comment = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8tags = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10num_score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的字段信息就是我们前面在网页中定位的 8 个字段信息,包括:name 表示 App 名称、volume 表示体积、download 表示下载数量。在这里定义好之后,我们在后续的爬取主程序中会利用到这些字段信息。
2.3.3. 爬取主程序
创建好 kuan 项目后,Scrapy 框架会自动生成爬取的部分代码,我们接下来就需要在 parse 方法中增加网页抓取的字段解析内容。
1class KuanspiderSpider(scrapy.Spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 name = 'kuan'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 allowed_domains = ['www.coolapk.com']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 start_urls = ['http://www.coolapk.com/']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 pass<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
打开主页 Dev Tools,找到每项抓取指标的节点位置,然后可以采用 CSS、Xpath、正则等方法进行提取解析,这些方法 Scrapy 都支持,可随意选择,这里我们选用 CSS 语法来定位节点,不过需要注意的是,Scrapy 的 CSS 语法和之前我们利用 pyquery 使用的 CSS 语法稍有不同,举几个例子,对比说明一下。
首先,我们定位到第一个 APP 的主页 URL 节点,可以看到 URL 节点位于 class 属性为app_left_list的 div 节点下的 a 节点中,其 href 属性就是我们需要的 URL 信息,这里是相对地址,拼接后就是完整的 URL。
接着我们进入酷安详情页,选择 App 名称并进行定位,可以看到 App 名称节点位于 class 属性为.detail_app_title的 p 节点的文本中。
定位到这两个节点之后,我们就可以使用 CSS 提取字段信息了,这里对比一下常规写法和 Scrapy 中的写法:
1# 常规写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2url = item('.app_left_list>a').attr('href')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3name = item('.list_app_title').text()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4# Scrapy 写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5url = item.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6name = item.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
可以看到,要获取 href 或者 text 属性,需要用 :: 表示,比如获取 text,则用 ::text。extract_first() 表示提取第一个元素,如果有多个元素,则用 extract() 。接着,我们就可以参照写出 8 个字段信息的解析代码。
首先,我们需要在主页提取 App 的 URL 列表,然后再进入每个 App 的详情页进一步提取 8 个字段信息。
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = content.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 url = response.urljoin(url) # 拼接相对 url 为绝对 url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 yield scrapy.Request(url,callback=self.parse_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,利用 response.urljoin() 方法将提取出的相对 URL 拼接为完整的 URL,然后利用 scrapy.Request() 方法构造每个 App 详情页的请求,这里我们传递两个参数:url 和 callback,url 为详情页 URL,callback 是回调函数,它将主页 URL 请求返回的响应 response 传给专门用来解析字段内容的 parse_url() 方法,如下所示:
1def parse_url(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 item = KuanItem()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 item['name'] = response.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 results = self.get_comment(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 item['download'] = results[1]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 item['follow'] = results[2]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 item['comment'] = results[3]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 item['tags'] = self.get_tags(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 item['score'] = response.css('.rank_num::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 num_score = response.css('.apk_rank_p1::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 item['num_score'] = re.search('共(.*?)个评分',num_score).group(1)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 yield item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15def get_comment(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 messages = response.css('.apk_topba_message::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages) # \s+ 表示匹配任意空白字符一次以上<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 if result: # 不为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 results = list(result[0]) # 提取出list 中第一个元素<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 return results<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22def get_tags(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 data = response.css('.apk_left_span2')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24 tags = [item.css('::text').extract_first() for item in data]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25 return tags<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,单独定义了 get_comment() 和 get_tags() 两个方法.
get_comment() 方法通过正则匹配提取 volume、download、follow、comment 四个字段信息,正则匹配结果如下:
1result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(result) # 输出第一页的结果信息<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3# 结果如下:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4[('21.74M', '5218万', '2.4万', '5.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5[('75.53M', '2768万', '2.3万', '3.0万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6[('46.21M', '1686万', '2.3万', '3.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7[('54.77M', '1603万', '3.8万', '4.9万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8[('3.32M', '1530万', '1.5万', '3343')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9[('75.07M', '1127万', '1.6万', '2.2万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10[('92.70M', '1108万', '9167', '1.3万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11[('68.94M', '1072万', '5718', '9869')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12[('61.45M', '935万', '1.1万', '1.6万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13[('23.96M', '925万', '4157', '1956')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
然后利用 result[0]、result[1] 等分别提取出四项信息,以 volume 为例,输出第一页的提取结果:
1item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(item['volume'])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 321.74M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 475.53M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 546.21M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 654.77M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 73.32M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 875.07M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 992.70M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1068.94M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1161.45M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1223.96M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这样一来,第一页 10 款 App 的所有字段信息都被成功提取出来,然后返回到 yied item 生成器中,我们输出一下它的内容:
1[<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2{'name': '酷安', 'volume': '21.74M', 'download': '5218万', 'follow': '2.4万', 'comment': '5.4万', 'tags': "['酷市场', '酷安', '市场', 'coolapk', '装机必备']", 'score': '4.4', 'num_score': '1.4万'}, <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3{'name': '微信', 'volume': '75.53M', 'download': '2768万', 'follow': '2.3万', 'comment': '3.0万', 'tags': "['微信', 'qq', '腾讯', 'tencent', '即时聊天', '装机必备']",'score': '2.3', 'num_score': '1.1万'},<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
2.3.4. 分页爬取
以上,我们爬取了第一页内容,接下去需要遍历爬取全部 610 页的内容,这里有两种思路:
这里,我们分别写出两种方法的解析代码,第一种方法很简单,直接接着 parse 方法继续添加以下几行代码即可:
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 ...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 next_page = response.css('.pagination li:nth-child(8) a::attr(href)').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 url = response.urljoin(next_page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />8 yield scrapy.Request(url,callback=self.parse )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二种方法,我们在最开头的 parse() 方法前,定义一个 start_requests() 方法,用来批量生成 610 页的 URL,然后通过 scrapy.Request() 方法中的 callback 参数,传递给下面的 parse() 方法进行解析。
1def start_requests(self):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 pages = []<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for page in range(1,610): # 一共有610页<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = 'https://www.coolapk.com/apk/?page=%s'%page<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 page = scrapy.Request(url,callback=self.parse)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 pages.append(page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 return pages<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上就是全部页面的爬取思路,爬取成功后,我们需要存储下来。这里,我面选择存储到 MongoDB 中,不得不说,相比 MySQL,MongoDB 要方便省事很多。
2.3.5. 存储结果
我们在 pipelines.py 程序中,定义数据存储方法,MongoDB 的一些参数,比如地址和数据库名称,需单独存放在 settings.py 设置文件中去,然后在 pipelines 程序中进行调用即可。
1import pymongo<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2class MongoPipeline(object):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 def __init__(self,mongo_url,mongo_db):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 self.mongo_url = mongo_url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 self.mongo_db = mongo_db<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 @classmethod<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 def from_crawler(cls,crawler):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 return cls(<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 mongo_url = crawler.settings.get('MONGO_URL'),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 mongo_db = crawler.settings.get('MONGO_DB')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 def open_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 self.client = pymongo.MongoClient(self.mongo_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 self.db = self.client[self.mongo_db]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 def process_item(self,item,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 name = item.__class__.__name__<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 self.db[name].insert(dict(item))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 return item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 def close_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 self.client.close()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
首先,我们定义一个 MongoPipeline()存储类,里面定义了几个方法,简单进行一下说明:
from crawler() 是一个类方法,用 @class method 标识,这个方法的作用主要是用来获取我们在 settings.py 中设置的这几项参数:
1MONGO_URL = 'localhost'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2MONGO_DB = 'KuAn'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3ITEM_PIPELINES = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 'kuan.pipelines.MongoPipeline': 300,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
open_spider() 方法主要进行一些初始化操作 ,在 Spider 开启时,这个方法就会被调用 。
process_item() 方法是最重要的方法,实现插入数据到 MongoDB 中。
完成上述代码以后,输入下面一行命令就可以开始整个爬虫的抓取和存储过程了,单机跑的话,6000 个网页需要不少时间才能完成,保持耐心。
1scrapy crawl kuan<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,还有两点补充:
第一,为了减轻网站压力,我们最好在每个请求之间设置几秒延时,可以在 KuanSpider() 方法开头出,加入以下几行代码:
1custom_settings = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 "DOWNLOAD_DELAY": 3, # 延迟3s,默认是0,即不延迟<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 "CONCURRENT_REQUESTS_PER_DOMAIN": 8 # 每秒默认并发8次,可适当降低<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二,为了更好监控爬虫程序运行,有必要设置输出日志文件,可以通过 Python 自带的 logging 包实现:
1import logging<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3logging.basicConfig(filename='kuan.log',filemode='w',level=logging.WARNING,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4logging.warning("warn message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5logging.error("error message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的 level 参数表示警告级别,严重程度从低到高分别是:DEBUG < INFO < WARNING < ERROR < CRITICAL,如果想日志文件不要记录太多内容,可以设置高一点的级别,这里设置为 WARNING,意味着只有 WARNING 级别以上的信息才会输出到日志中去。
添加 datefmt 参数是为了在每条日志前面加具体的时间,这点很有用处。
以上,我们就完成了整个数据的抓取,有了数据我们就可以着手进行分析,不过这之前还需简单地对数据做一下清洗和处理。
3. 数据清洗处理
首先,我们从 MongoDB 中读取数据并转化为 DataFrame,然后查看一下数据的基本情况。
1def parse_kuan():<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 client = pymongo.MongoClient(host='localhost', port=27017)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 db = client['KuAn']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 collection = db['KuAnItem']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 # 将数据库数据转为DataFrame<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 data = pd.DataFrame(list(collection.find()))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 print(data.head())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 print(df.shape)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 print(df.info())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 print(df.describe())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
从 data.head() 输出的前 5 行数据中可以看到,除了 score 列是 float 格式以外,其他列都是 object 文本类型。
comment、download、follow、num_score 这 5 列数据中部分行带有「万」字后缀,需要将字符去掉再转换为数值型;volume 体积列,则分别带有「M」和「K」后缀,为了统一大小,则需将「K」除以 1024,转换为 「M」体积。
整个数据一共有 6086 行 x 8 列,每列均没有缺失值。
df.describe() 方法对 score 列做了基本统计,可以看到,所有 App 的平均得分是 3.9 分(5 分制),最低得分 1.6 分,最高得分 4.8 分。
下面,我们将以上几列文本型数据转换为数值型数据,代码实现如下:
1def data_processing(df):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2#处理'comment','download','follow','num_score','volume' 5列数据,将单位万转换为单位1,再转换为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 str = '_ori'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 cols = ['comment','download','follow','num_score','volume']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 for col in cols:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 colori = col+str<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 df[colori] = df[col] # 复制保留原始列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 if not (col == 'volume'):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 df[col] = clean_symbol(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 else:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 df[col] = clean_symbol2(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 # 将download单独转换为万单位<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 df['download'] = df['download'].apply(lambda x:x/10000)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 # 批量转为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 df = df.apply(pd.to_numeric,errors='ignore')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18def clean_symbol(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 # 将字符“万”替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 con = df[col].str.contains('万$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('万','')) * 10000<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25def clean_symbol2(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />26 # 字符M替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />27 df[col] = df[col].str.replace('M$','')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />28 # 体积为K的除以 1024 转换为M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />29 con = df[col].str.contains('K$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />30 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('K$',''))/1024<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />31 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />32 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上,就完成了几列文本型数据的转换,我们再来查看一下基本情况:
download 列为 App 下载数量,下载量最多的 App 有 5190 万次,最少的为 0 (很少很少),平均下载次数为 14 万次;从中可以看出以下几点信息:
以上,就完成了基本的数据清洗处理过程,下一期将对这6000多款App进行探索性分析,看看有多少佳软神器你没有使用过哦。 查看全部
scrapy分页抓取网页 6000 多款 App,看我如何搞定她们并将其洗白白~
项目文件创建好以后,我们就可以开始写爬虫程序了。
首先,需要在 items.py 文件中,预先定义好要爬取的字段信息名称,如下所示:
1class KuanItem(scrapy.Item):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2# define the fields for your item here like:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3name = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4volume = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5download = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6follow = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7comment = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8tags = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10num_score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的字段信息就是我们前面在网页中定位的 8 个字段信息,包括:name 表示 App 名称、volume 表示体积、download 表示下载数量。在这里定义好之后,我们在后续的爬取主程序中会利用到这些字段信息。
2.3.3. 爬取主程序
创建好 kuan 项目后,Scrapy 框架会自动生成爬取的部分代码,我们接下来就需要在 parse 方法中增加网页抓取的字段解析内容。
1class KuanspiderSpider(scrapy.Spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 name = 'kuan'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 allowed_domains = ['www.coolapk.com']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 start_urls = ['http://www.coolapk.com/']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 pass<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
打开主页 Dev Tools,找到每项抓取指标的节点位置,然后可以采用 CSS、Xpath、正则等方法进行提取解析,这些方法 Scrapy 都支持,可随意选择,这里我们选用 CSS 语法来定位节点,不过需要注意的是,Scrapy 的 CSS 语法和之前我们利用 pyquery 使用的 CSS 语法稍有不同,举几个例子,对比说明一下。
首先,我们定位到第一个 APP 的主页 URL 节点,可以看到 URL 节点位于 class 属性为app_left_list的 div 节点下的 a 节点中,其 href 属性就是我们需要的 URL 信息,这里是相对地址,拼接后就是完整的 URL。
接着我们进入酷安详情页,选择 App 名称并进行定位,可以看到 App 名称节点位于 class 属性为.detail_app_title的 p 节点的文本中。
定位到这两个节点之后,我们就可以使用 CSS 提取字段信息了,这里对比一下常规写法和 Scrapy 中的写法:
1# 常规写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2url = item('.app_left_list>a').attr('href')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3name = item('.list_app_title').text()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4# Scrapy 写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5url = item.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6name = item.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
可以看到,要获取 href 或者 text 属性,需要用 :: 表示,比如获取 text,则用 ::text。extract_first() 表示提取第一个元素,如果有多个元素,则用 extract() 。接着,我们就可以参照写出 8 个字段信息的解析代码。
首先,我们需要在主页提取 App 的 URL 列表,然后再进入每个 App 的详情页进一步提取 8 个字段信息。
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = content.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 url = response.urljoin(url) # 拼接相对 url 为绝对 url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 yield scrapy.Request(url,callback=self.parse_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,利用 response.urljoin() 方法将提取出的相对 URL 拼接为完整的 URL,然后利用 scrapy.Request() 方法构造每个 App 详情页的请求,这里我们传递两个参数:url 和 callback,url 为详情页 URL,callback 是回调函数,它将主页 URL 请求返回的响应 response 传给专门用来解析字段内容的 parse_url() 方法,如下所示:
1def parse_url(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 item = KuanItem()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 item['name'] = response.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 results = self.get_comment(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 item['download'] = results[1]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 item['follow'] = results[2]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 item['comment'] = results[3]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 item['tags'] = self.get_tags(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 item['score'] = response.css('.rank_num::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 num_score = response.css('.apk_rank_p1::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 item['num_score'] = re.search('共(.*?)个评分',num_score).group(1)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 yield item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15def get_comment(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 messages = response.css('.apk_topba_message::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages) # \s+ 表示匹配任意空白字符一次以上<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 if result: # 不为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 results = list(result[0]) # 提取出list 中第一个元素<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 return results<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22def get_tags(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 data = response.css('.apk_left_span2')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24 tags = [item.css('::text').extract_first() for item in data]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25 return tags<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,单独定义了 get_comment() 和 get_tags() 两个方法.
get_comment() 方法通过正则匹配提取 volume、download、follow、comment 四个字段信息,正则匹配结果如下:
1result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(result) # 输出第一页的结果信息<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3# 结果如下:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4[('21.74M', '5218万', '2.4万', '5.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5[('75.53M', '2768万', '2.3万', '3.0万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6[('46.21M', '1686万', '2.3万', '3.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7[('54.77M', '1603万', '3.8万', '4.9万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8[('3.32M', '1530万', '1.5万', '3343')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9[('75.07M', '1127万', '1.6万', '2.2万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10[('92.70M', '1108万', '9167', '1.3万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11[('68.94M', '1072万', '5718', '9869')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12[('61.45M', '935万', '1.1万', '1.6万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13[('23.96M', '925万', '4157', '1956')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
然后利用 result[0]、result[1] 等分别提取出四项信息,以 volume 为例,输出第一页的提取结果:
1item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(item['volume'])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 321.74M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 475.53M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 546.21M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 654.77M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 73.32M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 875.07M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 992.70M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1068.94M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1161.45M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1223.96M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这样一来,第一页 10 款 App 的所有字段信息都被成功提取出来,然后返回到 yied item 生成器中,我们输出一下它的内容:
1[<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2{'name': '酷安', 'volume': '21.74M', 'download': '5218万', 'follow': '2.4万', 'comment': '5.4万', 'tags': "['酷市场', '酷安', '市场', 'coolapk', '装机必备']", 'score': '4.4', 'num_score': '1.4万'}, <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3{'name': '微信', 'volume': '75.53M', 'download': '2768万', 'follow': '2.3万', 'comment': '3.0万', 'tags': "['微信', 'qq', '腾讯', 'tencent', '即时聊天', '装机必备']",'score': '2.3', 'num_score': '1.1万'},<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
2.3.4. 分页爬取
以上,我们爬取了第一页内容,接下去需要遍历爬取全部 610 页的内容,这里有两种思路:
这里,我们分别写出两种方法的解析代码,第一种方法很简单,直接接着 parse 方法继续添加以下几行代码即可:
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 ...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 next_page = response.css('.pagination li:nth-child(8) a::attr(href)').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 url = response.urljoin(next_page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />8 yield scrapy.Request(url,callback=self.parse )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二种方法,我们在最开头的 parse() 方法前,定义一个 start_requests() 方法,用来批量生成 610 页的 URL,然后通过 scrapy.Request() 方法中的 callback 参数,传递给下面的 parse() 方法进行解析。
1def start_requests(self):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 pages = []<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for page in range(1,610): # 一共有610页<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = 'https://www.coolapk.com/apk/?page=%s'%page<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 page = scrapy.Request(url,callback=self.parse)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 pages.append(page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 return pages<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上就是全部页面的爬取思路,爬取成功后,我们需要存储下来。这里,我面选择存储到 MongoDB 中,不得不说,相比 MySQL,MongoDB 要方便省事很多。
2.3.5. 存储结果
我们在 pipelines.py 程序中,定义数据存储方法,MongoDB 的一些参数,比如地址和数据库名称,需单独存放在 settings.py 设置文件中去,然后在 pipelines 程序中进行调用即可。
1import pymongo<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2class MongoPipeline(object):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 def __init__(self,mongo_url,mongo_db):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 self.mongo_url = mongo_url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 self.mongo_db = mongo_db<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 @classmethod<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 def from_crawler(cls,crawler):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 return cls(<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 mongo_url = crawler.settings.get('MONGO_URL'),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 mongo_db = crawler.settings.get('MONGO_DB')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 def open_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 self.client = pymongo.MongoClient(self.mongo_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 self.db = self.client[self.mongo_db]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 def process_item(self,item,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 name = item.__class__.__name__<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 self.db[name].insert(dict(item))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 return item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 def close_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 self.client.close()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
首先,我们定义一个 MongoPipeline()存储类,里面定义了几个方法,简单进行一下说明:
from crawler() 是一个类方法,用 @class method 标识,这个方法的作用主要是用来获取我们在 settings.py 中设置的这几项参数:
1MONGO_URL = 'localhost'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2MONGO_DB = 'KuAn'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3ITEM_PIPELINES = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 'kuan.pipelines.MongoPipeline': 300,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
open_spider() 方法主要进行一些初始化操作 ,在 Spider 开启时,这个方法就会被调用 。
process_item() 方法是最重要的方法,实现插入数据到 MongoDB 中。
完成上述代码以后,输入下面一行命令就可以开始整个爬虫的抓取和存储过程了,单机跑的话,6000 个网页需要不少时间才能完成,保持耐心。
1scrapy crawl kuan<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,还有两点补充:
第一,为了减轻网站压力,我们最好在每个请求之间设置几秒延时,可以在 KuanSpider() 方法开头出,加入以下几行代码:
1custom_settings = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 "DOWNLOAD_DELAY": 3, # 延迟3s,默认是0,即不延迟<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 "CONCURRENT_REQUESTS_PER_DOMAIN": 8 # 每秒默认并发8次,可适当降低<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二,为了更好监控爬虫程序运行,有必要设置输出日志文件,可以通过 Python 自带的 logging 包实现:
1import logging<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3logging.basicConfig(filename='kuan.log',filemode='w',level=logging.WARNING,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4logging.warning("warn message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5logging.error("error message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的 level 参数表示警告级别,严重程度从低到高分别是:DEBUG < INFO < WARNING < ERROR < CRITICAL,如果想日志文件不要记录太多内容,可以设置高一点的级别,这里设置为 WARNING,意味着只有 WARNING 级别以上的信息才会输出到日志中去。
添加 datefmt 参数是为了在每条日志前面加具体的时间,这点很有用处。
以上,我们就完成了整个数据的抓取,有了数据我们就可以着手进行分析,不过这之前还需简单地对数据做一下清洗和处理。
3. 数据清洗处理
首先,我们从 MongoDB 中读取数据并转化为 DataFrame,然后查看一下数据的基本情况。
1def parse_kuan():<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 client = pymongo.MongoClient(host='localhost', port=27017)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 db = client['KuAn']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 collection = db['KuAnItem']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 # 将数据库数据转为DataFrame<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 data = pd.DataFrame(list(collection.find()))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 print(data.head())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 print(df.shape)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 print(df.info())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 print(df.describe())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
从 data.head() 输出的前 5 行数据中可以看到,除了 score 列是 float 格式以外,其他列都是 object 文本类型。
comment、download、follow、num_score 这 5 列数据中部分行带有「万」字后缀,需要将字符去掉再转换为数值型;volume 体积列,则分别带有「M」和「K」后缀,为了统一大小,则需将「K」除以 1024,转换为 「M」体积。
整个数据一共有 6086 行 x 8 列,每列均没有缺失值。
df.describe() 方法对 score 列做了基本统计,可以看到,所有 App 的平均得分是 3.9 分(5 分制),最低得分 1.6 分,最高得分 4.8 分。
下面,我们将以上几列文本型数据转换为数值型数据,代码实现如下:
1def data_processing(df):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2#处理'comment','download','follow','num_score','volume' 5列数据,将单位万转换为单位1,再转换为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 str = '_ori'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 cols = ['comment','download','follow','num_score','volume']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 for col in cols:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 colori = col+str<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 df[colori] = df[col] # 复制保留原始列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 if not (col == 'volume'):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 df[col] = clean_symbol(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 else:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 df[col] = clean_symbol2(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 # 将download单独转换为万单位<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 df['download'] = df['download'].apply(lambda x:x/10000)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 # 批量转为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 df = df.apply(pd.to_numeric,errors='ignore')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18def clean_symbol(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 # 将字符“万”替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 con = df[col].str.contains('万$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('万','')) * 10000<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25def clean_symbol2(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />26 # 字符M替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />27 df[col] = df[col].str.replace('M$','')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />28 # 体积为K的除以 1024 转换为M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />29 con = df[col].str.contains('K$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />30 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('K$',''))/1024<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />31 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />32 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上,就完成了几列文本型数据的转换,我们再来查看一下基本情况:
download 列为 App 下载数量,下载量最多的 App 有 5190 万次,最少的为 0 (很少很少),平均下载次数为 14 万次;从中可以看出以下几点信息:
以上,就完成了基本的数据清洗处理过程,下一期将对这6000多款App进行探索性分析,看看有多少佳软神器你没有使用过哦。
爬虫 | 如何构建技术文章聚合平台(一)
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-08 00:10
博客地址:
背景
说到爬虫,大多数程序员想到的是scrapy这样受人欢迎的框架。scrapy的确不错,而且有很强大的生态圈,有gerapy等优秀的可视化界面。但是,它还是有一些不能做到的事情,例如在页面上做翻页点击操作、移动端抓取等等。对于这些新的需求,可以用Selenium、Puppeteer、Appium这些自动化测试框架绕开繁琐的动态内容,直接模拟用户操作进行抓取。可惜的是,这些框架不是专门的爬虫框架,不能对爬虫进行集中管理,因此对于一个多达数十个爬虫的大型项目来说有些棘手。
Crawlab是一个基于Celery的分布式通用爬虫管理平台,擅长将不同编程语言编写的爬虫整合在一处,方便监控和管理。Crawlab有精美的可视化界面,能对多个爬虫进行运行和管理。任务调度引擎是本身支持分布式架构的Celery,因此Crawlab可以天然集成分布式爬虫。有一些朋友认为Crawlab只是一个任务调度引擎,其实这样认为并不完全正确。Crawlab是类似Gerapy这样的专注于爬虫的管理平台。
本文将介绍如何使用Crawlab和Puppeteer抓取主流的技术博客文章,然后用Flask+Vue搭建一个小型的技术文章聚合平台。
Crawlab
在前一篇文章《分布式通用爬虫管理平台Crawlab》已介绍了Crawlab的架构以及安装使用,这里快速介绍一下如何安装、运行、使用Crawlab。(感兴趣的同学可以去作者的掘金主页查看)
安装
到Crawlab的Github Repo用克隆一份到本地。
<p>git clone https://github.com/tikazyq/crawlab
复制代码</p>
安装相应的依赖包和库。
<p>cd crawlab
<br />
# 安装python依赖
pip install -r crawlab/requirements
<br />
# 安装前端依赖
cd frontend
npm install
复制代码</p>
安装mongodb和redis-server。Crawlab将用MongoDB作为结果集以及运行操作的储存方式,Redis作为Celery的任务队列,因此需要安装这两个数据库。
运行
在运行之前需要对Crawlab进行一些配置,配置文件为config.py。
<p># project variables
PROJECT_SOURCE_FILE_FOLDER = '/Users/yeqing/projects/crawlab/spiders' # 爬虫源码根目录
PROJECT_DEPLOY_FILE_FOLDER = '/var/crawlab' # 爬虫部署根目录
PROJECT_LOGS_FOLDER = '/var/logs/crawlab' # 日志目录
PROJECT_TMP_FOLDER = '/tmp' # 临时文件目录
<br />
# celery variables
BROKER_URL = 'redis://192.168.99.100:6379/0' # 中间者URL,连接redis
CELERY_RESULT_BACKEND = 'mongodb://192.168.99.100:27017/' # CELERY后台URL
CELERY_MONGODB_BACKEND_SETTINGS = {
'database': 'crawlab_test',
'taskmeta_collection': 'tasks_celery',
}
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
<br />
# flower variables
FLOWER_API_ENDPOINT = 'http://localhost:5555/api' # Flower服务地址
<br />
# database variables
MONGO_HOST = '192.168.99.100'
MONGO_PORT = 27017
MONGO_DB = 'crawlab_test'
<br />
# flask variables
DEBUG = True
FLASK_HOST = '127.0.0.1'
FLASK_PORT = 8000
复制代码</p>
启动后端API,也就是一个Flask App,可以直接启动,或者用gunicorn代替。
<p>cd ../crawlab
python app.py
复制代码</p>
启动Flower服务(抱歉目前集成Flower到App服务中,必须单独启动来获取节点信息,后面的版本不需要这个操作)。
<p>python ./bin/run_flower.py
复制代码</p>
启动本地Worker。在其他节点中如果想只是想执行任务的话,只需要启动这一个服务就可以了。
<p>python ./bin/run_worker.py
复制代码</p>
启动前端服务器。
<p>cd ../frontend
npm run serve
复制代码</p>
使用
首页Home中可以看到总任务数、总爬虫数、在线节点数和总部署数,以及过去30天的任务运行数量。
点击侧边栏的Spiders或者上方到Spiders数,可以进入到爬虫列表页。
这些是爬虫源码根目录PROJECT_SOURCE_FILE_FOLDER下的爬虫。Crawlab会自动扫描该目录下的子目录,将子目录看作一个爬虫。Action列下有一些操作选项,点击部署Deploy按钮将爬虫部署到所有在线节点中。部署成功后,点击运行Run按钮,触发抓取任务。这时,任务应该已经在执行了。点击侧边栏的Tasks到任务列表,可以看到已经调度过的爬虫任务。
基本使用就是这些,但是Crawlab还能做到更多,大家可以进一步探索,详情请见Github。
Puppeteer
Puppeteer是谷歌开源的基于Chromium和NodeJS的自动化测试工具,可以很方便的让程序模拟用户的操作,对浏览器进行程序化控制。Puppeteer有一些常用操作,例如点击,鼠标移动,滑动,截屏,下载文件等等。另外,Puppeteer很类似Selenium,可以定位浏览器中网页元素,将其数据抓取下来。因此,Puppeteer也成为了新的爬虫利器。
相对于Selenium,Puppeteer是新的开源项目,而且是谷歌开发,可以使用很多新的特性。对于爬虫来说,如果前端知识足够的话,写数据抓取逻辑简直不能再简单。正如其名字一样,我们是在操作木偶人来帮我们抓取数据,是不是很贴切?
掘金上已经有很多关于Puppeteer的教程了(爬虫利器 Puppeteer 实战、Puppeteer 与 Chrome Headless —— 从入门到爬虫),这里只简单介绍一下Puppeteer的安装和使用。
安装
安装很简单,就一行npm install命令,npm会自动下载Chromium并安装,这个时间会比较长。为了让安装好的puppeteer模块能够被所有nodejs爬虫所共享,我们在PROJECT_DEPLOY_FILE_FOLDER目录下安装node的包。
<p># PROJECT_DEPLOY_FILE_FOLDER变量值
cd /var/crawlab
<br />
# 安装puppeteer
npm i puppeteer
<br />
# 安装mongodb
npm i mongodb
复制代码</p>
安装mongodb是为了后续的数据库操作。
使用
以下是Copy/Paste的一段用Puppeteer访问简书然后截屏的代码,非常简洁。
<p>const puppeteer = require('puppeteer');
<br />
(async () => {
const browser = await (puppeteer.launch());
const page = await browser.newPage();
await page.goto('https://www.jianshu.com/u/40909ea33e50');
await page.screenshot({
path: 'jianshu.png',
type: 'png',
// quality: 100, 只对jpg有效
fullPage: true,
// 指定区域截图,clip和fullPage两者只能设置一个
// clip: {
// x: 0,
// y: 0,
// width: 1000,
// height: 40
// }
});
browser.close();
})();
复制代码</p>
关于Puppeteer的常用操作,请移步《我常用的puppeteer爬虫api》。
编写爬虫
啰嗦了这么久,终于到了万众期待的爬虫时间了。Talk is cheap, show me the code!咦?我们不是已经Show了不少代码了么...
由于我们的目标是建立一个技术文章聚合平台,我们需要去各大技术网站抓取文章。资源当然是越多越好。作为展示用,我们将抓取下面几个具有代表性的网站:
研究发现这三个网站都是由Ajax获取文章列表,生成动态内容以作为传统的分页替代。这对于Puppeteer来说很容易处理,因为Puppeteer绕开了解析Ajax这一部分,浏览器会自动处理这样的操作和请求,我们只着重关注数据获取就行了。三个网站的抓取策略基本相同,我们以掘金为例着重讲解。
掘金
首先是引入Puppeteer和打开网页。
<p>const puppeteer = require('puppeteer');
const MongoClient = require('mongodb').MongoClient;
<br />
(async () => {
// browser
const browser = await (puppeteer.launch({
headless: true
}));
<br />
// define start url
const url = 'https://juejin.im';
<br />
// start a new page
const page = await browser.newPage();
<br />
...
<br />
})();
复制代码</p>
headless设置为true可以让浏览器以headless的方式运行,也就是指浏览器不用在界面中打开,它会在后台运行,用户是看不到浏览器的。browser.newPage()将新生成一个标签页。后面的操作基本就围绕着生成的page来进行。
接下来我们让浏览器导航到start url。
<p> ...
<br />
// navigate to url
try {
await page.goto(url, {waitUntil: 'domcontentloaded'});
await page.waitFor(2000);
} catch (e) {
console.error(e);
<br />
// close browser
browser.close();
<br />
// exit code 1 indicating an error happened
code = 1;
process.emit("exit ");
process.reallyExit(code);
<br />
return
}
<br />
...
复制代码</p>
这里trycatch的操作是为了处理浏览器访问超时的错误。当访问超时时,设置exit code为1表示该任务失败了,这样Crawlab会将该任务状态设置为FAILURE。 查看全部
爬虫 | 如何构建技术文章聚合平台(一)
博客地址:
背景
说到爬虫,大多数程序员想到的是scrapy这样受人欢迎的框架。scrapy的确不错,而且有很强大的生态圈,有gerapy等优秀的可视化界面。但是,它还是有一些不能做到的事情,例如在页面上做翻页点击操作、移动端抓取等等。对于这些新的需求,可以用Selenium、Puppeteer、Appium这些自动化测试框架绕开繁琐的动态内容,直接模拟用户操作进行抓取。可惜的是,这些框架不是专门的爬虫框架,不能对爬虫进行集中管理,因此对于一个多达数十个爬虫的大型项目来说有些棘手。
Crawlab是一个基于Celery的分布式通用爬虫管理平台,擅长将不同编程语言编写的爬虫整合在一处,方便监控和管理。Crawlab有精美的可视化界面,能对多个爬虫进行运行和管理。任务调度引擎是本身支持分布式架构的Celery,因此Crawlab可以天然集成分布式爬虫。有一些朋友认为Crawlab只是一个任务调度引擎,其实这样认为并不完全正确。Crawlab是类似Gerapy这样的专注于爬虫的管理平台。
本文将介绍如何使用Crawlab和Puppeteer抓取主流的技术博客文章,然后用Flask+Vue搭建一个小型的技术文章聚合平台。
Crawlab
在前一篇文章《分布式通用爬虫管理平台Crawlab》已介绍了Crawlab的架构以及安装使用,这里快速介绍一下如何安装、运行、使用Crawlab。(感兴趣的同学可以去作者的掘金主页查看)
安装
到Crawlab的Github Repo用克隆一份到本地。
<p>git clone https://github.com/tikazyq/crawlab
复制代码</p>
安装相应的依赖包和库。
<p>cd crawlab
<br />
# 安装python依赖
pip install -r crawlab/requirements
<br />
# 安装前端依赖
cd frontend
npm install
复制代码</p>
安装mongodb和redis-server。Crawlab将用MongoDB作为结果集以及运行操作的储存方式,Redis作为Celery的任务队列,因此需要安装这两个数据库。
运行
在运行之前需要对Crawlab进行一些配置,配置文件为config.py。
<p># project variables
PROJECT_SOURCE_FILE_FOLDER = '/Users/yeqing/projects/crawlab/spiders' # 爬虫源码根目录
PROJECT_DEPLOY_FILE_FOLDER = '/var/crawlab' # 爬虫部署根目录
PROJECT_LOGS_FOLDER = '/var/logs/crawlab' # 日志目录
PROJECT_TMP_FOLDER = '/tmp' # 临时文件目录
<br />
# celery variables
BROKER_URL = 'redis://192.168.99.100:6379/0' # 中间者URL,连接redis
CELERY_RESULT_BACKEND = 'mongodb://192.168.99.100:27017/' # CELERY后台URL
CELERY_MONGODB_BACKEND_SETTINGS = {
'database': 'crawlab_test',
'taskmeta_collection': 'tasks_celery',
}
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
<br />
# flower variables
FLOWER_API_ENDPOINT = 'http://localhost:5555/api' # Flower服务地址
<br />
# database variables
MONGO_HOST = '192.168.99.100'
MONGO_PORT = 27017
MONGO_DB = 'crawlab_test'
<br />
# flask variables
DEBUG = True
FLASK_HOST = '127.0.0.1'
FLASK_PORT = 8000
复制代码</p>
启动后端API,也就是一个Flask App,可以直接启动,或者用gunicorn代替。
<p>cd ../crawlab
python app.py
复制代码</p>
启动Flower服务(抱歉目前集成Flower到App服务中,必须单独启动来获取节点信息,后面的版本不需要这个操作)。
<p>python ./bin/run_flower.py
复制代码</p>
启动本地Worker。在其他节点中如果想只是想执行任务的话,只需要启动这一个服务就可以了。
<p>python ./bin/run_worker.py
复制代码</p>
启动前端服务器。
<p>cd ../frontend
npm run serve
复制代码</p>
使用
首页Home中可以看到总任务数、总爬虫数、在线节点数和总部署数,以及过去30天的任务运行数量。
点击侧边栏的Spiders或者上方到Spiders数,可以进入到爬虫列表页。
这些是爬虫源码根目录PROJECT_SOURCE_FILE_FOLDER下的爬虫。Crawlab会自动扫描该目录下的子目录,将子目录看作一个爬虫。Action列下有一些操作选项,点击部署Deploy按钮将爬虫部署到所有在线节点中。部署成功后,点击运行Run按钮,触发抓取任务。这时,任务应该已经在执行了。点击侧边栏的Tasks到任务列表,可以看到已经调度过的爬虫任务。
基本使用就是这些,但是Crawlab还能做到更多,大家可以进一步探索,详情请见Github。
Puppeteer
Puppeteer是谷歌开源的基于Chromium和NodeJS的自动化测试工具,可以很方便的让程序模拟用户的操作,对浏览器进行程序化控制。Puppeteer有一些常用操作,例如点击,鼠标移动,滑动,截屏,下载文件等等。另外,Puppeteer很类似Selenium,可以定位浏览器中网页元素,将其数据抓取下来。因此,Puppeteer也成为了新的爬虫利器。
相对于Selenium,Puppeteer是新的开源项目,而且是谷歌开发,可以使用很多新的特性。对于爬虫来说,如果前端知识足够的话,写数据抓取逻辑简直不能再简单。正如其名字一样,我们是在操作木偶人来帮我们抓取数据,是不是很贴切?
掘金上已经有很多关于Puppeteer的教程了(爬虫利器 Puppeteer 实战、Puppeteer 与 Chrome Headless —— 从入门到爬虫),这里只简单介绍一下Puppeteer的安装和使用。
安装
安装很简单,就一行npm install命令,npm会自动下载Chromium并安装,这个时间会比较长。为了让安装好的puppeteer模块能够被所有nodejs爬虫所共享,我们在PROJECT_DEPLOY_FILE_FOLDER目录下安装node的包。
<p># PROJECT_DEPLOY_FILE_FOLDER变量值
cd /var/crawlab
<br />
# 安装puppeteer
npm i puppeteer
<br />
# 安装mongodb
npm i mongodb
复制代码</p>
安装mongodb是为了后续的数据库操作。
使用
以下是Copy/Paste的一段用Puppeteer访问简书然后截屏的代码,非常简洁。
<p>const puppeteer = require('puppeteer');
<br />
(async () => {
const browser = await (puppeteer.launch());
const page = await browser.newPage();
await page.goto('https://www.jianshu.com/u/40909ea33e50');
await page.screenshot({
path: 'jianshu.png',
type: 'png',
// quality: 100, 只对jpg有效
fullPage: true,
// 指定区域截图,clip和fullPage两者只能设置一个
// clip: {
// x: 0,
// y: 0,
// width: 1000,
// height: 40
// }
});
browser.close();
})();
复制代码</p>
关于Puppeteer的常用操作,请移步《我常用的puppeteer爬虫api》。
编写爬虫
啰嗦了这么久,终于到了万众期待的爬虫时间了。Talk is cheap, show me the code!咦?我们不是已经Show了不少代码了么...
由于我们的目标是建立一个技术文章聚合平台,我们需要去各大技术网站抓取文章。资源当然是越多越好。作为展示用,我们将抓取下面几个具有代表性的网站:
研究发现这三个网站都是由Ajax获取文章列表,生成动态内容以作为传统的分页替代。这对于Puppeteer来说很容易处理,因为Puppeteer绕开了解析Ajax这一部分,浏览器会自动处理这样的操作和请求,我们只着重关注数据获取就行了。三个网站的抓取策略基本相同,我们以掘金为例着重讲解。
掘金
首先是引入Puppeteer和打开网页。
<p>const puppeteer = require('puppeteer');
const MongoClient = require('mongodb').MongoClient;
<br />
(async () => {
// browser
const browser = await (puppeteer.launch({
headless: true
}));
<br />
// define start url
const url = 'https://juejin.im';
<br />
// start a new page
const page = await browser.newPage();
<br />
...
<br />
})();
复制代码</p>
headless设置为true可以让浏览器以headless的方式运行,也就是指浏览器不用在界面中打开,它会在后台运行,用户是看不到浏览器的。browser.newPage()将新生成一个标签页。后面的操作基本就围绕着生成的page来进行。
接下来我们让浏览器导航到start url。
<p> ...
<br />
// navigate to url
try {
await page.goto(url, {waitUntil: 'domcontentloaded'});
await page.waitFor(2000);
} catch (e) {
console.error(e);
<br />
// close browser
browser.close();
<br />
// exit code 1 indicating an error happened
code = 1;
process.emit("exit ");
process.reallyExit(code);
<br />
return
}
<br />
...
复制代码</p>
这里trycatch的操作是为了处理浏览器访问超时的错误。当访问超时时,设置exit code为1表示该任务失败了,这样Crawlab会将该任务状态设置为FAILURE。
scrapy分页分页抓取网页过程中的生命周期有哪些?
网站优化 • 优采云 发表了文章 • 0 个评论 • 76 次浏览 • 2022-08-31 18:00
scrapy分页抓取网页过程中的生命周期有哪些?查看下面handlers.py文件定义的scrapy任务循环时间片:pythonconfig.py定义各个方法,参数自己定义,可以是config,自己写的,也可以用scrapy官方提供的定义的,如果自己编写,一定要弄懂下面几个函数中的参数含义future*:执行该scrapy任务循环时的阻塞方法(如果阻塞则保持阻塞,否则会开始。
比如说一个循环循环500个timeout,如果当前是阻塞的状态,则会一直执行500-500-500-500-500这样循环500次)if-else:代表循环结束条件(所有的所有的方法会自动初始化好并且开始工作,如果出现其他条件阻塞,则阻塞程序中的每一行代码,并继续处理剩余的500行代码,如果是某一个方法自动初始化好并且处理了其他条件,则代表执行该方法中所有的语句,如果出现其他函数是阻塞的,则自动执行代码,处理剩余的500行代码)define:选择执行spider到一个关于抓取对象的类中spider=scrapy.spider(fields=('title','score','vote_num','capture_time','author','date','summary','min_delta','num_stories','max_stories','request_type','post_type','source','post','vote_mails','posts','text','binary','follow_tom','lookup','update','userid','user_id','email','qq','group','email_code','update_fields','distributor','custom_code','user_code','userid','user_pass','user_pass','custom_profile','posttext','user_pass','user_name','user_profile','author','start_time','end_time','fail_time','repost_time','spider','form','tag','message','action','timeout','create_request','response','post','encoding','timeout','post_long','post_long','response_source','request_source','trigger','spider_method','directory','gettick','trailing_timeout','data_item','default_layout','round','rounded','post_timeout','local_timeout','author_last_trigger','post_timeout','author_last_trigger','vote_num','capture_time','vote_num','vote_delta','summary','。 查看全部
scrapy分页分页抓取网页过程中的生命周期有哪些?

scrapy分页抓取网页过程中的生命周期有哪些?查看下面handlers.py文件定义的scrapy任务循环时间片:pythonconfig.py定义各个方法,参数自己定义,可以是config,自己写的,也可以用scrapy官方提供的定义的,如果自己编写,一定要弄懂下面几个函数中的参数含义future*:执行该scrapy任务循环时的阻塞方法(如果阻塞则保持阻塞,否则会开始。

比如说一个循环循环500个timeout,如果当前是阻塞的状态,则会一直执行500-500-500-500-500这样循环500次)if-else:代表循环结束条件(所有的所有的方法会自动初始化好并且开始工作,如果出现其他条件阻塞,则阻塞程序中的每一行代码,并继续处理剩余的500行代码,如果是某一个方法自动初始化好并且处理了其他条件,则代表执行该方法中所有的语句,如果出现其他函数是阻塞的,则自动执行代码,处理剩余的500行代码)define:选择执行spider到一个关于抓取对象的类中spider=scrapy.spider(fields=('title','score','vote_num','capture_time','author','date','summary','min_delta','num_stories','max_stories','request_type','post_type','source','post','vote_mails','posts','text','binary','follow_tom','lookup','update','userid','user_id','email','qq','group','email_code','update_fields','distributor','custom_code','user_code','userid','user_pass','user_pass','custom_profile','posttext','user_pass','user_name','user_profile','author','start_time','end_time','fail_time','repost_time','spider','form','tag','message','action','timeout','create_request','response','post','encoding','timeout','post_long','post_long','response_source','request_source','trigger','spider_method','directory','gettick','trailing_timeout','data_item','default_layout','round','rounded','post_timeout','local_timeout','author_last_trigger','post_timeout','author_last_trigger','vote_num','capture_time','vote_num','vote_delta','summary','。
scrapy分页抓取网页实战-11celery执行过程-陈愚依
网站优化 • 优采云 发表了文章 • 0 个评论 • 51 次浏览 • 2022-08-24 17:10
scrapy分页抓取网页实战-scrapyv1。11celery执行过程-陈愚依@棱镜scrapy深度学习抓取youtube热门视频-room来自捷克的html5前端框架d3。js读取电商网站的js文件,css文件一个root权限,也要看你是否登录不会的请问我,我每天做这个重复工作好多,求大神指导。
之前用scrapy在聚合类网站扒了很多html。
研究了一下百度文库的问答机制,可能是因为你的doc和res主题的关系,
sqlite不支持ajax请求,有时候请求相关的信息可能需要跳转,你可以添加pythonsite对象来实现跳转,就是类似调用对应dom元素来调用ajax请求(不同dom元素调用方法一样)。ajax之前还支持过轮询:djangositejsperfmcom目前github的sitemap是不支持轮询的,你可以做一个类似于百度+py文库的网页,每当显示完相关文件需要记录一下就跳转。
pythoncelery抓取scrapy文件
试过scrapy,
微软安装的python版本,python2,python3的执行环境不一样,用的java环境都不一样。 查看全部
scrapy分页抓取网页实战-11celery执行过程-陈愚依
scrapy分页抓取网页实战-scrapyv1。11celery执行过程-陈愚依@棱镜scrapy深度学习抓取youtube热门视频-room来自捷克的html5前端框架d3。js读取电商网站的js文件,css文件一个root权限,也要看你是否登录不会的请问我,我每天做这个重复工作好多,求大神指导。
之前用scrapy在聚合类网站扒了很多html。

研究了一下百度文库的问答机制,可能是因为你的doc和res主题的关系,
sqlite不支持ajax请求,有时候请求相关的信息可能需要跳转,你可以添加pythonsite对象来实现跳转,就是类似调用对应dom元素来调用ajax请求(不同dom元素调用方法一样)。ajax之前还支持过轮询:djangositejsperfmcom目前github的sitemap是不支持轮询的,你可以做一个类似于百度+py文库的网页,每当显示完相关文件需要记录一下就跳转。

pythoncelery抓取scrapy文件
试过scrapy,
微软安装的python版本,python2,python3的执行环境不一样,用的java环境都不一样。
scrapy.7.1官方文档:基于python2的异步框架,1.x的写法和1
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-07-27 16:04
scrapy分页抓取网页原理是找到页码所在的行,然后继续往下抓取就好了关于代码规范请看scrapy1.7.1官方文档
我一直认为scrapy是基于python2的异步框架,1.x的写法和1.x非常不同,但是2和2之间的用法是一样的!同步和异步还是要看你需要抓取的页面,1.x时代往往数据都在session内部,这里这个是数据分析基本功,要以自己实际项目为基准了。2.x更多的使用异步,因为将数据传递给spider的时候可以异步地传递到wsgi服务,如果是io请求则正常处理,spider可以处理数据库,epoll或者其他的。
3.spider也可以做爬虫,这个倒是没见过有人写成middleware设计的,因为页面容易多次执行,甚至不同的项目可能数据结构也不一样,是很难拿过来进行控制的,一般会找个开源框架来控制,或者参考如pyspider/zopextend·github等。
1.可以分页这一块我认为和hadoop类似2.如果java或者python语言基础也没什么问题的话,我推荐3.x版本,
因为看到这个问题知乎上很多相关回答,就回答一下吧。分页抓取其实就是根据重定向来抓取网页数据,一般实现原理都差不多,主要分为页面重定向和cookie重定向,下面以httpclient为例子解释一下。分页的抓取其实原理不复杂,只要实现了request模块的cookie重定向,并且指定了参数即可。根据我接触到的分页抓取来看,重定向一般分为两种:连续的提交请求和单次请求下面分别介绍下两种方式1.连续的提交请求重定向方式a.在客户端实现重定向,连续提交多个请求。
例如:每次请求之间都存在关联性,这样抓取效率高,后端会对每个请求发送多个数据包,合并数据包即可抓取多个数据包。b.单次的请求如果被定义一个等间隔(默认3毫秒)请求,那么抓取会在不同时间点继续向后端返回相同请求。具体请求的返回结果表中会详细列出以返回的数据包中每个包的返回状态,对应不同的返回信息,同时返回或者在后端返回同一页抓取方式的请求中,请求方式会在request.urls中列出返回的地址和response.statuscode值。
2.单次的请求后端发送单次request,a.需要python的selector类,它包含最适合某个请求的选择器,例如book,一个状态字符串就会得到最适合的book.book.headers#请求的状态字符串withopen(file.text,'w')asf:book=pd.read_html(selector.headers)#post请求的状态字符串,返回是post方式book.encoding='utf-8'#error输出输入的error值#lambda函数headers这里说明一。 查看全部
scrapy.7.1官方文档:基于python2的异步框架,1.x的写法和1
scrapy分页抓取网页原理是找到页码所在的行,然后继续往下抓取就好了关于代码规范请看scrapy1.7.1官方文档
我一直认为scrapy是基于python2的异步框架,1.x的写法和1.x非常不同,但是2和2之间的用法是一样的!同步和异步还是要看你需要抓取的页面,1.x时代往往数据都在session内部,这里这个是数据分析基本功,要以自己实际项目为基准了。2.x更多的使用异步,因为将数据传递给spider的时候可以异步地传递到wsgi服务,如果是io请求则正常处理,spider可以处理数据库,epoll或者其他的。

3.spider也可以做爬虫,这个倒是没见过有人写成middleware设计的,因为页面容易多次执行,甚至不同的项目可能数据结构也不一样,是很难拿过来进行控制的,一般会找个开源框架来控制,或者参考如pyspider/zopextend·github等。
1.可以分页这一块我认为和hadoop类似2.如果java或者python语言基础也没什么问题的话,我推荐3.x版本,

因为看到这个问题知乎上很多相关回答,就回答一下吧。分页抓取其实就是根据重定向来抓取网页数据,一般实现原理都差不多,主要分为页面重定向和cookie重定向,下面以httpclient为例子解释一下。分页的抓取其实原理不复杂,只要实现了request模块的cookie重定向,并且指定了参数即可。根据我接触到的分页抓取来看,重定向一般分为两种:连续的提交请求和单次请求下面分别介绍下两种方式1.连续的提交请求重定向方式a.在客户端实现重定向,连续提交多个请求。
例如:每次请求之间都存在关联性,这样抓取效率高,后端会对每个请求发送多个数据包,合并数据包即可抓取多个数据包。b.单次的请求如果被定义一个等间隔(默认3毫秒)请求,那么抓取会在不同时间点继续向后端返回相同请求。具体请求的返回结果表中会详细列出以返回的数据包中每个包的返回状态,对应不同的返回信息,同时返回或者在后端返回同一页抓取方式的请求中,请求方式会在request.urls中列出返回的地址和response.statuscode值。
2.单次的请求后端发送单次request,a.需要python的selector类,它包含最适合某个请求的选择器,例如book,一个状态字符串就会得到最适合的book.book.headers#请求的状态字符串withopen(file.text,'w')asf:book=pd.read_html(selector.headers)#post请求的状态字符串,返回是post方式book.encoding='utf-8'#error输出输入的error值#lambda函数headers这里说明一。
scrapy分页抓取网页的相关细节介绍可以参考中的文档和社区joshuaseng
网站优化 • 优采云 发表了文章 • 0 个评论 • 61 次浏览 • 2022-07-26 11:01
scrapy分页抓取网页的相关细节介绍可以参考scrapy中的文档和社区joshuaseng:scrapy分页抓取网页的相关细节介绍如果你理解executor的执行流程的话,在scrapy中的pythonexecutor、httpexecutor、gevent的执行流程都一样,都是将参数包装为相应的streams,并不断分包,分好后在内存中进行拷贝。
只是本质上是scrapy的作用是解决异步抓取,抓取更多网页时用gevent更多,那这里介绍的executor虽然是抓取同一个网页,但它也是一个异步任务队列的实现,主要用来解决每个抓取的streams有不同的队列,这种blockingstreamscheduler模型不好处理分页抓取,为了更好的处理分页抓取任务,scrapy提供了自定义的异步任务队列catchtasks,executor可以直接用它来执行异步任务,只是有时会对异步任务的处理结果产生一些影响,很多人都知道在抓取完成后回退时executor将会处理分页任务,这对于处理分页相关请求会带来影响,因为分页是重复抓取过程,在收到重复信息时executor会返回一个redisobject.structure(name="",root=0)来返回相应的字典。
所以我们会出现redisobject.structure("'',ip='10.99.139.137',ds=[''])这种格式的处理结果并不安全,而且会报错。executor的源码在scrapy/executor/python.py中。但在python2.x中,在抓取过程中gevent协程是跟executor一起执行的,在2.x中,scrapy使用scrapyqtcore替代了gevent,它是scrapy协程管理进程的基本框架,不同gevent协程和scrapyqtcore绑定在不同的客户端上,executor是pythonpython代码代理的实现,每个gevent协程可以有多个实例,在实际中可以使用--gevent属性来指定所有gevent协程实例的线程数量,然后在qtcore执行本地任务来主动调用实例来执行一系列本地任务,使用“cpus”给cpu个数,cpu的每一个时钟周期就执行一个本地任务。
这样qtcore中全都是executor实例而不是gevent协程,这样做的好处是scrapy1.6版本引入了mutable_scheduler,也就是scrapy自带异步分页机制,从而不需要手动启动gevent协程。本文以分页抓取一个chinaz网站下的某个热门旅游城市的热门人物的个人的信息,这些信息会以文本列表的形式展示给我们,点击会跳转到该个人的主页,在本文中,我只抓取旅游城市的热门人物的个人信息。
fromscrapy.requestimporturlfromscrapy.cookieimportkeywordsfromscrapy.exceptionsimportexcusefroms。 查看全部
scrapy分页抓取网页的相关细节介绍可以参考中的文档和社区joshuaseng
scrapy分页抓取网页的相关细节介绍可以参考scrapy中的文档和社区joshuaseng:scrapy分页抓取网页的相关细节介绍如果你理解executor的执行流程的话,在scrapy中的pythonexecutor、httpexecutor、gevent的执行流程都一样,都是将参数包装为相应的streams,并不断分包,分好后在内存中进行拷贝。

只是本质上是scrapy的作用是解决异步抓取,抓取更多网页时用gevent更多,那这里介绍的executor虽然是抓取同一个网页,但它也是一个异步任务队列的实现,主要用来解决每个抓取的streams有不同的队列,这种blockingstreamscheduler模型不好处理分页抓取,为了更好的处理分页抓取任务,scrapy提供了自定义的异步任务队列catchtasks,executor可以直接用它来执行异步任务,只是有时会对异步任务的处理结果产生一些影响,很多人都知道在抓取完成后回退时executor将会处理分页任务,这对于处理分页相关请求会带来影响,因为分页是重复抓取过程,在收到重复信息时executor会返回一个redisobject.structure(name="",root=0)来返回相应的字典。
所以我们会出现redisobject.structure("'',ip='10.99.139.137',ds=[''])这种格式的处理结果并不安全,而且会报错。executor的源码在scrapy/executor/python.py中。但在python2.x中,在抓取过程中gevent协程是跟executor一起执行的,在2.x中,scrapy使用scrapyqtcore替代了gevent,它是scrapy协程管理进程的基本框架,不同gevent协程和scrapyqtcore绑定在不同的客户端上,executor是pythonpython代码代理的实现,每个gevent协程可以有多个实例,在实际中可以使用--gevent属性来指定所有gevent协程实例的线程数量,然后在qtcore执行本地任务来主动调用实例来执行一系列本地任务,使用“cpus”给cpu个数,cpu的每一个时钟周期就执行一个本地任务。

这样qtcore中全都是executor实例而不是gevent协程,这样做的好处是scrapy1.6版本引入了mutable_scheduler,也就是scrapy自带异步分页机制,从而不需要手动启动gevent协程。本文以分页抓取一个chinaz网站下的某个热门旅游城市的热门人物的个人的信息,这些信息会以文本列表的形式展示给我们,点击会跳转到该个人的主页,在本文中,我只抓取旅游城市的热门人物的个人信息。
fromscrapy.requestimporturlfromscrapy.cookieimportkeywordsfromscrapy.exceptionsimportexcusefroms。
多逛逛,看看。我也只能从文档里学到的
网站优化 • 优采云 发表了文章 • 0 个评论 • 104 次浏览 • 2022-07-20 17:04
scrapy分页抓取网页:scrapy1。1。0中文文档网页浏览器分页:scrapy1。1。0中文文档scrapy-python文档|scrapy中文文档scrapy-siteapp文档|scrapy中文文档scrapy-crawler文档|scrapy中文文档scrapy-python中文文档scrapy-startpd爬虫框架中文文档:scrapy-siteapp中文文档scrapy-python中文文档。
scrapy0.3.0中文文档-scrapy中文文档和scrapy0.01版本-scrapy中文文档基于python的web服务器|scrapydocs
项目给出的docid
参考百度镜像站的csdn镜像站的scrapy教程,很全。我也是看别人的博客学到的。
可以直接用scrapy爬虫思想结合爬虫框架构建python爬虫框架scrapy
scrapy有中文文档
目前正在学习中,欢迎大家讨论。
能不能有个人手把手教学的,免费的,
《scrapy中文文档》发布在爬虫资源网上scrapy中文文档:
如果你把http_response想象成scrapy对应文档的html_response的话,这个问题就迎刃而解了。
多逛逛,看看。
我也只能从文档里学到点皮毛。毕竟这本书大半不是我写的。只是入门水平。
让我看一下scrapy的运行过程,再让我看看http的协议有多简单,再让我运行用java写的代码,最后看看scrapy部署。大概需要一个月。 查看全部
多逛逛,看看。我也只能从文档里学到的
scrapy分页抓取网页:scrapy1。1。0中文文档网页浏览器分页:scrapy1。1。0中文文档scrapy-python文档|scrapy中文文档scrapy-siteapp文档|scrapy中文文档scrapy-crawler文档|scrapy中文文档scrapy-python中文文档scrapy-startpd爬虫框架中文文档:scrapy-siteapp中文文档scrapy-python中文文档。
scrapy0.3.0中文文档-scrapy中文文档和scrapy0.01版本-scrapy中文文档基于python的web服务器|scrapydocs
项目给出的docid
参考百度镜像站的csdn镜像站的scrapy教程,很全。我也是看别人的博客学到的。

可以直接用scrapy爬虫思想结合爬虫框架构建python爬虫框架scrapy
scrapy有中文文档
目前正在学习中,欢迎大家讨论。
能不能有个人手把手教学的,免费的,

《scrapy中文文档》发布在爬虫资源网上scrapy中文文档:
如果你把http_response想象成scrapy对应文档的html_response的话,这个问题就迎刃而解了。
多逛逛,看看。
我也只能从文档里学到点皮毛。毕竟这本书大半不是我写的。只是入门水平。
让我看一下scrapy的运行过程,再让我看看http的协议有多简单,再让我运行用java写的代码,最后看看scrapy部署。大概需要一个月。
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 126 次浏览 • 2022-06-27 07:45
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。

使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 67 次浏览 • 2022-06-25 15:17
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置

Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果

V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页

V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 69 次浏览 • 2022-06-24 23:29
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 64 次浏览 • 2022-06-23 19:10
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 75 次浏览 • 2022-06-22 03:09
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 54 次浏览 • 2022-06-21 06:55
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
网站优化 • 优采云 发表了文章 • 0 个评论 • 67 次浏览 • 2022-06-21 02:22
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。 查看全部
scrapy分页抓取网页 还在焦头烂额裸写Scrapy?这个神器让你90秒内配好一个爬虫
背景
爬虫是一件有趣的事情,让你可以通过爬虫程序自动化的将网上的信息抓取下来,免去了很多人工操作。在一些优质爬虫框架出来之前,开发者们还是通过简单的网络请求+网页解析器的方式来开发爬虫程序,例如 Python 的 requests + BeautifulSoup,高级一点的爬虫程序还会加入数据储存的模块,例如 MySQL、MongoDB。这种方式开发效率低,稳定性不佳,要开发好一个完备的、生产可用的爬虫可能需要好几个小时。我将这种方式称作 非框架爬虫。
2011 年,基于 Twisted 编写的 Scrapy 爬虫框架横空出世,突然被大众熟知,成为了首屈一指的全能的高性能爬虫异步框架。Scrapy 抽象出了几个核心模块,让开发者将主要注意力放在爬虫抓取逻辑上,而不用管数据下载、页面解析、任务调度等比较繁琐的模块。开发好一个生产可用的 Scrapy 爬虫,简单的可能只需要十几分钟,复杂的可能需要 1 小时以上。当然,我们还有其他很多优秀框架,例如 PySpider、Colly 等。我将这种爬虫称作 框架爬虫。框架爬虫解放了生产力,现在很多企业将框架爬虫改造后应用在生产环境中,大规模抓取数据。
然而,对于需要抓成百上千个网站的爬虫需求来说,框架爬虫可能就有些心有余而力不足了,编写爬虫成了体力活。例如,如果平均开发一个框架爬虫需要 20 分钟,如果一个全职爬虫开发工程师每天工作 8 个小时,那么开发 1000 个网站就需要 20000 分钟,333 个小时,42 个工作日,近 2 个月。当然,我们可以雇佣 10 个全职爬虫开发工程师,但这同样需要 4 个工作日才能完成(如下图)。
这同样是比较低效的。为了克服这个效率问题,可配置爬虫 应运而生。
可配置爬虫介绍
可配置爬虫(Configurable Spider) 正如其名字表示的,就是可以配置抓取规则的爬虫。可配置爬虫是一种高度抽象的爬虫程序,开发人员不用编写爬虫代码,只需要将需要抓取网页地址、字段、属性写在配置文件或数据库中,让特殊的爬虫程序根据配置去抓取数据。可配置爬虫将爬虫代码进一步抽象成了配置信息,简化了爬虫开发的流程。爬虫开发者只需要做相应的配置就可以完成爬虫的开发。因此,开发者可以通过可配置爬虫,大规模的编写爬虫程序(如下图)。
这样的方式让抓取成百上千的网站成了可能,一个熟练的爬虫配置员一天可以配置 1000 个新闻网站的爬虫。这对于有舆情监控需求的企业来说非常重要,因为可配置爬虫提高了生产力,让单位工作时间成本降低,提升了开发效率,方便后续的舆情分析和人工智能产品开发。很多企业都是自己研发的可配置爬虫(可能叫法会有些不一样,但实质是一个东西),然后雇佣一些爬虫配置员专门负责配置爬虫。
市面上免费开源的可配置爬虫框架并不多。比较早的有微软大神崔庆才开发的 Gerapy,属于一个爬虫管理平台,能够根据配置规则生成 Scrapy 项目文件。另一个比较新的可配置爬虫框架是 Crawlab(其实 Crawlab 不是可配置爬虫框架,而是一个灵活度很高的爬虫管理平台),在 v0.4.0 中发布了可配置爬虫。另外还有一个基于 Golang 的开源框架 Ferret ,很有意思,编写爬虫跟写 SQL 一样简单。其他还有一些商业产品,但根据用户反馈后都觉得专业度不高,不能满足生产需求。
可配置爬虫的诞生,主要是爬虫的模式比较单一,无非就是列表页+详情页的组合(如下图),或者仅仅列表页。当然还有稍微复杂一点的通用爬虫,这些也可以通过规则配置来完成。
Crawlab 可配置爬虫
我们今天主要介绍的是 Crawlab 的可配置爬虫。我们之前在 这篇文章 中有所介绍,但并没有深入讲解如何应用到实战中。今天,我们着重讲解一下。如果对 Crawlabb 的可配置爬虫比较陌生,请参考可配置爬虫的 文档。
可配置爬虫实战
实战部分的所有案例是作者用 Crawlab 的 官方 Demo 平台 通过可配置爬虫功能编写并完成抓取的,涵盖了新闻、金融、汽车、书籍、视频、搜索引擎、程序员社区等领域(见下图)。下面将介绍其中的几个,所有例子均在 官方 Demo 平台 上,均可以注册账号登录查看。
百度(搜索 "Crawlab")
爬虫地址:#/spiders/5e27d055b8f9c90019f42a83
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: http://www.baidu.com/s?wd=crawlab<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: ""<br /> list_xpath: //*[contains(@class, "c-container")]<br /> page_css: ""<br /> page_xpath: //*[@id="page"]//a[@class="n"][last()]<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: ""<br /> xpath: .//h3/a<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: ""<br /> xpath: .//h3/a<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: ""<br /> xpath: .//*[@class="c-abstract"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
SegmentFault(最新文章)
爬虫地址:#/spiders/5e27d116b8f9c90019f42a87
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://segmentfault.com/newest<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .news-list > .news-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: h4.news__item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .news-img<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: abstract<br /> css: .article-excerpt<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
亚马逊中国(搜索"手机")
爬虫地址:#/spiders/5e27e157b8f9c90019f42afb
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://www.amazon.cn/s%3Fk%3D ... %3Bbr />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .s-result-item<br /> list_xpath: ""<br /> page_css: .a-last > a<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: span.a-text-normal<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: .a-link-normal<br /> xpath: ""<br /> attr: href<br /> next_stage: ""<br /> remark: ""<br /> - name: price<br /> css: ""<br /> xpath: .//*[@class="a-price-whole"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: price_fraction<br /> css: ""<br /> xpath: .//*[@class="a-price-fraction"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: img<br /> css: .s-image-square-aspect > img<br /> xpath: ""<br /> attr: src<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
抓取结果
V2ex
爬虫地址:#/spiders/5e27dd67b8f9c90019f42ad9
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://v2ex.com/<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .cell.item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: href<br /> fields:<br /> - name: title<br /> css: a.topic-link<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: a.topic-link<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: replies<br /> css: .count_livid<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="markdown_body"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> AUTOTHROTTLE_ENABLED: "true"<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/79.0.3945.117 Safari/537.36<br />
抓取结果
36氪
爬虫地址:#/spiders/5e27ec82b8f9c90019f42b59
爬虫配置
Spiderfile
version: 0.4.4<br />engine: scrapy<br />start_url: https://36kr.com/information/web_news<br />start_stage: list<br />stages:<br />- name: list<br /> is_list: true<br /> list_css: .kr-flow-article-item<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: title<br /> css: .article-item-title<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: url<br /> css: body<br /> xpath: ""<br /> attr: href<br /> next_stage: detail<br /> remark: ""<br /> - name: abstract<br /> css: body<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: author<br /> css: .kr-flow-bar-author<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br /> - name: time<br /> css: .kr-flow-bar-time<br /> xpath: ""<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />- name: detail<br /> is_list: false<br /> list_css: ""<br /> list_xpath: ""<br /> page_css: ""<br /> page_xpath: ""<br /> page_attr: ""<br /> fields:<br /> - name: content<br /> css: ""<br /> xpath: .//*[@class="common-width content articleDetailContent kr-rich-text-wrapper"]<br /> attr: ""<br /> next_stage: ""<br /> remark: ""<br />settings:<br /> ROBOTSTXT_OBEY: "false"<br /> USER_AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML,<br /> like Gecko) Chrome/78.0.3904.108 Safari/537.36<br />
实战爬虫一览爬虫名称爬虫类别
百度
列表页+分页
SegmentFault
列表页
CSDN
列表页+分页+详情页
V2ex
列表页+详情页
纵横
列表页
亚马逊中国
列表页+分页
雪球网
列表页+详情页
汽车之家
列表页+分页
豆瓣读书
列表页
36氪
列表页+详情页
腾讯视频
列表页
总结
Crawlab 的可配置爬虫非常方便,让程序员可以快速配置出自己需要的爬虫。配置上述 11 个爬虫总共花费了作者不到 40 分钟的时间(考虑到有反爬调试在里面),其中几个比较简单的爬虫在 1-2 分钟不到就配置完成了。而且作者一行代码也没有写,所有配置均在界面上完成。而且,Crawlab 可配置爬虫不仅支持在界面上的配置,同时还支持编写一个 Yaml 文件 Spiderfile 来完成配置(其实,所有的配置均可以映射到 Spiderfile 中)。Crawlab 可配置爬虫是基于 Scrapy 的,因此支持 Scrapy 绝大多数特性,可以通过 设置 来配置可配置爬虫的扩展属性,包括 USER_AGENT、ROBOTSTXT_OBEY 等等。为什么要用 Crawlab 作为可配置爬虫的首选呢?因为 Crawlab 可配置爬虫不仅能够配置爬虫,还能享受 Crawlab 爬虫管理平台的核心功能,包括任务调度、任务监控、定时任务、日志管理、消息通知等实用功能。而在后续开发中,Crawlab 开发组还将不断完善可配置爬虫,让其支持更多的功能,包括动态内容、更多的引擎、CrawlSpider 的实现等等。
要注意的是,不遵守 robots.txt 可能会造成法律风险,本文的实战爬虫均为学习交流用,切勿作为生产环境,任何滥用者自行承担法律责任。
参考
如果您觉得 Crawlab 对您的日常开发或公司有帮助,请加作者微信 tikazyq1 并注明 "Crawlab",作者会将你拉入群。欢迎在 Github 上进行 star,以及,如果遇到任何问题,请随时在 Github 上提 issue。另外,欢迎您对 Crawlab 做开发贡献。
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 83 次浏览 • 2022-06-19 01:25
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
【零基础学爬虫】scrapy实战:爬取知乎【所有用户】信息
网站优化 • 优采云 发表了文章 • 0 个评论 • 52 次浏览 • 2022-06-18 05:55
简介
知乎用户信息是非常大的,本文是一个scrapy实战:怎样抓取所有知乎用户信息。
爬取的思路如下图所示:
站点分析
本文以轮子哥为根节点(根节点可以随便选择),打开轮子哥的关注列表,并翻页查看关注列表:
翻页是一个AJAX请求,每页20个关注用户,并且有一些简要的用户信息
其中有一个是url-token,它是用来标识一个用户的,在上面截图中那个用户的主页url为:
其中就有url-token
项目实战
创建项目
思路分析
获取用户信息
首先需要获取用户的基本信息,这个基本信息可以通过请求类似下面的url获得:
{url_token}/following
而url_token可以在用户的关注列表中获取,上面url的页面类似于这个
另外,还有右侧获得赞同、被收藏、感谢的次数
从上述url获取用户基本信息后,接下来就是获取当前用户的关注列表:
获取用户的关注列表
这个列表存在翻页,经过分析得知关注列表的翻页是通过AJAX请求实现的,获取某页关注列表url如下所示:
%5B%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%ics&offset=0&limit=20
提取一下规律:
{url-token}/followees?include={include}&offset={offset}&limit=2{limit}
其中include为:
data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics
上面url返回时一个json,该json中包含了20个关注者,response数据如下图所示:
对于一个关注者,我们只需要获取该用户的url-token,通过url-token即可拼接出该用户的主页,也就可以获得该用户的基本信息,以及关注者列表等。
获取到url-token后,我们需要判断有没有下一页,如果有则进行翻页,在上图的后半部分还有一个字段:
翻到当前的最后一页,该字段如下图所示:
因此依据is_end字段就可以判断是否下一页关注者了,使用next字段的值即可获取下一页关注者的列表了。
思路整理
所以呢?在解析用户基本信息的同时,我们可以拿到用户的url_token,进一步我们也就可以拿到该用户的关注者列表,如此递爬取,基本可获得所有知乎用户的所有基本信息。
本次的pipline设置为:
源码和爬取的部分数据:
爬取的部分知乎用户数据
源码
爬取知乎用户的思路已经在文章介绍了,思路理好后,实现比较块。
主要是源码有点长,这里就不全贴出来了。需要源码的同学可以通过文末方式获取
扫描下方二维码,公众号菜鸟名企梦后台发送关键词“知乎”即可获取本文的完整源码和详细程序注释
长按下面二维码,
发送“知乎”即可获取完整源码
热 文推 荐
公众号后台发送“爬虫”,即可获取“零基础学爬虫专栏”系列文章
公众号后台回复“目录”:即可获取小编熬夜编排整理好的历史文章:
目录截图如下(可上下滑动):
喜欢就点「在看」吧 ! 查看全部
【零基础学爬虫】scrapy实战:爬取知乎【所有用户】信息
简介
知乎用户信息是非常大的,本文是一个scrapy实战:怎样抓取所有知乎用户信息。
爬取的思路如下图所示:
站点分析
本文以轮子哥为根节点(根节点可以随便选择),打开轮子哥的关注列表,并翻页查看关注列表:
翻页是一个AJAX请求,每页20个关注用户,并且有一些简要的用户信息
其中有一个是url-token,它是用来标识一个用户的,在上面截图中那个用户的主页url为:
其中就有url-token
项目实战
创建项目
思路分析
获取用户信息
首先需要获取用户的基本信息,这个基本信息可以通过请求类似下面的url获得:
{url_token}/following
而url_token可以在用户的关注列表中获取,上面url的页面类似于这个
另外,还有右侧获得赞同、被收藏、感谢的次数
从上述url获取用户基本信息后,接下来就是获取当前用户的关注列表:
获取用户的关注列表
这个列表存在翻页,经过分析得知关注列表的翻页是通过AJAX请求实现的,获取某页关注列表url如下所示:
%5B%5D.answer_count%2Carticles_count%2Cgender%2Cfollower_count%2Cis_followed%2Cis_following%2Cbadge%5B%3F(type%3Dbest_answerer)%ics&offset=0&limit=20
提取一下规律:
{url-token}/followees?include={include}&offset={offset}&limit=2{limit}
其中include为:
data[*].answer_count,articles_count,gender,follower_count,is_followed,is_following,badge[?(type=best_answerer)].topics
上面url返回时一个json,该json中包含了20个关注者,response数据如下图所示:
对于一个关注者,我们只需要获取该用户的url-token,通过url-token即可拼接出该用户的主页,也就可以获得该用户的基本信息,以及关注者列表等。
获取到url-token后,我们需要判断有没有下一页,如果有则进行翻页,在上图的后半部分还有一个字段:
翻到当前的最后一页,该字段如下图所示:
因此依据is_end字段就可以判断是否下一页关注者了,使用next字段的值即可获取下一页关注者的列表了。
思路整理
所以呢?在解析用户基本信息的同时,我们可以拿到用户的url_token,进一步我们也就可以拿到该用户的关注者列表,如此递爬取,基本可获得所有知乎用户的所有基本信息。
本次的pipline设置为:
源码和爬取的部分数据:
爬取的部分知乎用户数据
源码
爬取知乎用户的思路已经在文章介绍了,思路理好后,实现比较块。
主要是源码有点长,这里就不全贴出来了。需要源码的同学可以通过文末方式获取
扫描下方二维码,公众号菜鸟名企梦后台发送关键词“知乎”即可获取本文的完整源码和详细程序注释
长按下面二维码,
发送“知乎”即可获取完整源码
热 文推 荐
公众号后台发送“爬虫”,即可获取“零基础学爬虫专栏”系列文章
公众号后台回复“目录”:即可获取小编熬夜编排整理好的历史文章:
目录截图如下(可上下滑动):


喜欢就点「在看」吧 !
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
网站优化 • 优采云 发表了文章 • 0 个评论 • 80 次浏览 • 2022-06-18 05:54
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~ 查看全部
scrapy分页抓取网页 凭借这5步,我30分钟学会了Python爬虫!
在不同公司的许多人可能出于各种原因需要从Internet收集外部数据:分析竞争,汇总新闻摘要、跟踪特定市场的趋势,或者收集每日股票价格以建立预测模型……
无论你是数据科学家还是业务分析师,都可能时不时遇到这种情况,并问自己一个永恒的问题:我如何才能提取该网站的数据以进行市场分析?
提取网站数据及其结构的一种可能的免费方法是爬虫。
在本文中,你将了解如何通过Python轻松的完成数据爬虫任务。
什么是爬虫?
广义上讲,数据爬虫是指以编程方式提取网站数据并根据其需求进行结构化的过程。
许多公司正在使用数据爬虫来收集外部数据并支持其业务运营:这是当前在多个领域中普遍的做法。
我需要了解什么才能学习python中的数据抓取?
很简单,但是需要首先具备一些Python和HTML知识。
另外,需要了解两个非常有效的框架,例如,Scrapy或Selenium。
详细介绍
接下来,让我们学习如何将网站变成结构化数据!
为此,首先需要安装以下库:
如果你使用的是Anaconda,配置起来会非常简单,这些软件包都已预先安装。
如果不是使用Anaconda,需要通过如下命令安装工具包:
pip install requests<br />pip install beautifulsoup4<br />pip install lxml<br />pip install pandas<br />
我们要抓取哪些网站和数据?
这是爬虫过程中首先需要回答的问题。
本文就以爬取Premium Beauty News为例进行演示。
该以优质美容新闻为主,它发布了美容市场的最新趋势。
查看首页,你会看到我们要抓取的文章以网格形式组织。
多页面的组织如下:
当然,我们仅要提取出现在这些页面上的每篇文章的标题,我们将深入每个帖子并获取我们需要的详细内容,例如:
编码实践
前面,已经介绍了基本的内容以及需要用到的工具包。
接下来,就是正式编码实践的步骤。
首先,需要导入基础工具包:
import requests <br />from bs4 import BeautifulSoup <br />import pandas as pd <br />from tqdm import tqdm_notebook<br />
我通常定义一个函数来解析给定URL的每个页面的内容。
该函数将被多次调用,这里将他命名为parse_url:
def parse_url(url): <br /> response = requests.get(url)<br /> content = response.content <br /> parsed_response = BeautifulSoup(content, "lxml") <br /> return parsed_response<br />
提取每个帖子数据和元数据
首先,我将定义一个函数,该函数提取给定URL的每个帖子的数据(标题,日期,摘要等)。
然后,我们将遍历所有页面的for循环内调用此函数。
要构建我们的爬虫工具,我们首先必须了解页面的基本HTML逻辑和结构。以提取帖子的标题为例,讲解一下。
通过在Chrome检查器中检查此元素:
我们注意到标题出现在 article-title类的h1内。
使用BeautifulSoup提取页面内容后,可以使用find方法提取标题。
title = soup_post.find("h1", {"class": "article-title"}).text<br />
接下来,看一下日期:
该日期显示在一个span内,该范围本身显示在row sub-header类的标题内。
使用BeautifulSoup将其转换为代码非常容易:
datetime = soup_post.find("header", {"class": "row sub- header"}).find("span")["datetime"]<br />
下一步就是摘要:
它在article-intro的h2标签下:
abstract = soup_post.find("h2", {"class": "article-intro"}).text<br />
现在,需要爬取帖子的全文内容。如果已经理解了前面的内容,那么这部分会非常容易。
该内容在article-text类的div内的多个段落(p标签)中。
BeautifulSoup可以通过以下一种方式提取完整的文本。而不是遍历每个每个p标签、提取文本、然后将所有文本连接在一起。
content = soup_post.find("div", {"class": "article-text"}).text<br />
下面,让我们把它们放在同一个函数内看一下:
def extract_post_data(post_url):<br /> soup_post = parse_url(post_url)<br /> <br /> title = soup_post.find("h1", {"class": "article-title"}).text<br /> datetime = soup_post.find("header", {"class": "row sub-header"}).find("span")["datetime"]<br /> abstract = soup_post.find("h2", {"class": "article-intro"}).text<br /> content = soup_post.find("div", {"class": "article-text"}).text<br /> <br /> data = {<br /> "title": title,<br /> "datetime": datetime,<br /> "abstract": abstract,<br /> "content": content,<br /> "url": post_url<br /> }<br /> <br /> return data<br />
提取多个页面上的帖子URL
如果我们检查主页的源代码,会看到每个页面文章的标题:
可以看到,每10篇文章出现在1个post-style1 col-md-6标签下:
下面,提取每个页面的文章就很容易了:
url = "https://www.premiumbeautynews. ... %3Bbr />soup = parse_url(url)<br />section = soup.find("section", {"class": "content"})<br />posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br />
然后,对于每个单独的帖子,我们可以提取URL,该URL出现在h4标签内部。
我们将使用此URL调用我们先前定义的函数extract_post_data。
uri = post.find("h4").find("a")["href"]<br />
分页
在给定页面上提取帖子后,需要转到下一页并重复相同的操作。
如果查看分页,需要点击“下一个”按钮:
到达最后一页后,此按钮变为无效。
换句话说,当下一个按钮处于有效状态时,就需要执行爬虫操作,移至下一页并重复该操作。当按钮变为无效状态时,该过程应停止。
总结此逻辑,这将转换为以下代码:
next_button = ""<br />posts_data = []<br />count = 1<br />base_url = 'https://www.premiumbeautynews.com/'<br /><br />while next_button is not None:<br /> print(f"page number : {count}")<br /><br /> soup = parse_url(url)<br /> section = soup.find("section", {"class": "content"})<br /> posts = section.findAll("div", {"class": "post-style1 col-md-6"})<br /><br /> for post in tqdm_notebook(posts, leave=False):<br /> uri = post.find("h4").find("a")["href"]<br /> post_url = base_url + uri<br /> data = extract_post_data(post_url)<br /> posts_data.append(data)<br /> <br /> next_button = soup.find("p", {"class": "pagination"}).find("span", {"class": "next"})<br /> if next_button is not None:<br /> url = base_url + next_button.find("a")["href"]<br /> count += 1<br />
此循环完成后,将所有数据保存在posts_data中,可以将其转换为漂亮的DataFrames并导出为CSV或Excel文件。
df = pd.DataFrame(posts_data)<br />df.head()<br />
到这里,就把一个非结构化的网页转化成结构化的数据了!
往期精选
福利
最近我花费了半个月的时间,整理了1份理论+实践的计算机视觉入门教程,这或许是你见过最好的一份CV教程之一。独家打造、完全免费,需要的同学可以扫码添加我的个人微信,发送“CV”获取~
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构
网站优化 • 优采云 发表了文章 • 0 个评论 • 61 次浏览 • 2022-05-22 23:00
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构——html文件通过网页抓取的正则表达式匹配其中一段然后根据规则解析出所需的网页代码,这种思路一定要牢记next表达式是分页分页的html文件中一定会出现一个名为next的变量代表下一页,next变量指向的网页代码也是分页的网页源码例如我们分页抓取所有urls.py为json格式的页面代码,那么next变量指向的html文件中页码为0当我们一页页的访问时,前面某个网页点击不断滚动就会抓取到下一页,在进行next判断,如果next表达式为真,即next变量为true,就表示抓取到最新一页,否则返回我们刚才网页查看源码会发现,将抓取到的所有网页都和之前抓取到的网页网址对比,就可以知道页码是从0开始的,这就是我们认为的的next判断true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页以前学习的网页爬虫分页抓取html文件的格式其实是list({'页数':'page','页码':'page','网址':'../basic/'})查看源码,一次仅抓取一个页面,例如我们抓取a.txt这个html文件,当我们访问b.txt时,b.txt返回的页码是空,此时判断页码是从0开始的。
我们想尽可能的不抓取b.txt中的网址,而是只抓取a.txt中的网址那该怎么判断页码是从0开始呢?想象一下当你解析b.txt中的网址的时候,如果页码也是从0开始,那你就需要再抓取一个关联的b.txt中的页码,然后把每一页的网址传入上述函数,这个过程就是分页true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页这里要注意next变量的形式,当变量长度为0时,next变量会随机覆盖每一页的网址,不存在使用next重复定时抓取的问题重点是判断在每次循环中,网页的item是否是新页面重新定时抓取网页,如果不是,那么next判断true值为false,你可以抛弃循环过程,直接返回完整的页码去除开头几行代码在匹配网页源码的时候,一定要注意先看list({'页码':'page','页码':'page','网址':'../basic/'})的数组形式(不要想当然的写url),然后判断里面每一个元素对应的页码是否从0开始的然后再把每个元素和上面的页码进行比较判断出是从0开始循环抓取(为什么?试一下就知道了)。
关于scrapy的变量化判断,这里没有写具体什么,比如env下的变量keys可以认为是和它对应的html源码上述的判断过程只是html源码的变量化简单的对角色内存地址的判断,根据n。 查看全部
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构
scrapy分页抓取网页技术分析系列第四篇会做到一个固定的结构——html文件通过网页抓取的正则表达式匹配其中一段然后根据规则解析出所需的网页代码,这种思路一定要牢记next表达式是分页分页的html文件中一定会出现一个名为next的变量代表下一页,next变量指向的网页代码也是分页的网页源码例如我们分页抓取所有urls.py为json格式的页面代码,那么next变量指向的html文件中页码为0当我们一页页的访问时,前面某个网页点击不断滚动就会抓取到下一页,在进行next判断,如果next表达式为真,即next变量为true,就表示抓取到最新一页,否则返回我们刚才网页查看源码会发现,将抓取到的所有网页都和之前抓取到的网页网址对比,就可以知道页码是从0开始的,这就是我们认为的的next判断true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页以前学习的网页爬虫分页抓取html文件的格式其实是list({'页数':'page','页码':'page','网址':'../basic/'})查看源码,一次仅抓取一个页面,例如我们抓取a.txt这个html文件,当我们访问b.txt时,b.txt返回的页码是空,此时判断页码是从0开始的。
我们想尽可能的不抓取b.txt中的网址,而是只抓取a.txt中的网址那该怎么判断页码是从0开始呢?想象一下当你解析b.txt中的网址的时候,如果页码也是从0开始,那你就需要再抓取一个关联的b.txt中的页码,然后把每一页的网址传入上述函数,这个过程就是分页true值:若next变量为false,则不定时抓取下一页,否则不定时抓取上一页这里要注意next变量的形式,当变量长度为0时,next变量会随机覆盖每一页的网址,不存在使用next重复定时抓取的问题重点是判断在每次循环中,网页的item是否是新页面重新定时抓取网页,如果不是,那么next判断true值为false,你可以抛弃循环过程,直接返回完整的页码去除开头几行代码在匹配网页源码的时候,一定要注意先看list({'页码':'page','页码':'page','网址':'../basic/'})的数组形式(不要想当然的写url),然后判断里面每一个元素对应的页码是否从0开始的然后再把每个元素和上面的页码进行比较判断出是从0开始循环抓取(为什么?试一下就知道了)。
关于scrapy的变量化判断,这里没有写具体什么,比如env下的变量keys可以认为是和它对应的html源码上述的判断过程只是html源码的变量化简单的对角色内存地址的判断,根据n。
scrapy分页抓取网页视频还需要根据实际情况加上语句
网站优化 • 优采云 发表了文章 • 0 个评论 • 72 次浏览 • 2022-05-17 10:06
scrapy分页抓取网页视频很多时候我们都是需要分页的,比如我们要抓取某一页中的所有喜剧视频,对于分页抓取的时候,还需要对数据进行转换然后使用循环或其他方式。scrapy中的分页就是其中一种解决方案。大家都知道scrapy中不支持tabe情况,但也不可以一行代码完全处理分页,所以还需要加上taben形式的字符串和数据结构。
fromscrapy.contextimportcontextfromscrapy.collectionimportfilterfromscrapy.exceptionsimportexceptionclassmore_item(context):def__init__(self,item_path):"""definetheitempath.forexample:port=item_path,available_perpage=item_path,none=item_path...send_content=filter(item_path.select('.'),random=false)more_item=context(more_item=more_item)"""self.list_page=[item_path]self.exception=exception(more_item)上面代码大致框架就是这样,具体还需要根据实际情况加上语句。
header={'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','user-agent':'mozilla/5.0(windowsnt6.1;wow64)applewebkit/537.36(khtml,likegecko)chrome/67.0.3497.139safari/537.36'}content_length=len(self.res_data)defget_item(self,item_path):self.list_page=[]res=self.res_data[item_path].filter(item_path=item_path,random=true)defsend_content(self,sentence):print("sentence:",sentence)self.exception=exception(more_item=sentence)ifsentence.startswith(self.res_data.keywords(sentence.keyword_base)):item_dict=self.res_data[item_path].filter(content_dict=item_dict)item_list=list(item_dict)foriteminitem_dict:sub=item[item.keyword]print("port:",sub)print("available_perpage:",sub)print("none:",sub)else:print("0-5-4-6...")defget_tail_page(self,item_path):content_length=len(self.res_data)res=self.res_data[item_path].filter(content。 查看全部
scrapy分页抓取网页视频还需要根据实际情况加上语句
scrapy分页抓取网页视频很多时候我们都是需要分页的,比如我们要抓取某一页中的所有喜剧视频,对于分页抓取的时候,还需要对数据进行转换然后使用循环或其他方式。scrapy中的分页就是其中一种解决方案。大家都知道scrapy中不支持tabe情况,但也不可以一行代码完全处理分页,所以还需要加上taben形式的字符串和数据结构。
fromscrapy.contextimportcontextfromscrapy.collectionimportfilterfromscrapy.exceptionsimportexceptionclassmore_item(context):def__init__(self,item_path):"""definetheitempath.forexample:port=item_path,available_perpage=item_path,none=item_path...send_content=filter(item_path.select('.'),random=false)more_item=context(more_item=more_item)"""self.list_page=[item_path]self.exception=exception(more_item)上面代码大致框架就是这样,具体还需要根据实际情况加上语句。
header={'accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8','user-agent':'mozilla/5.0(windowsnt6.1;wow64)applewebkit/537.36(khtml,likegecko)chrome/67.0.3497.139safari/537.36'}content_length=len(self.res_data)defget_item(self,item_path):self.list_page=[]res=self.res_data[item_path].filter(item_path=item_path,random=true)defsend_content(self,sentence):print("sentence:",sentence)self.exception=exception(more_item=sentence)ifsentence.startswith(self.res_data.keywords(sentence.keyword_base)):item_dict=self.res_data[item_path].filter(content_dict=item_dict)item_list=list(item_dict)foriteminitem_dict:sub=item[item.keyword]print("port:",sub)print("available_perpage:",sub)print("none:",sub)else:print("0-5-4-6...")defget_tail_page(self,item_path):content_length=len(self.res_data)res=self.res_data[item_path].filter(content。
Python抓取公众号文章并生成pdf文件保存到本地
网站优化 • 优采云 发表了文章 • 0 个评论 • 66 次浏览 • 2022-05-10 01:24
前面一篇文章由于做的时间比较仓促还留下了几个问题:
分页的时候出现了数据重复,
什么时候爬取完了数据,根本不知道
那些文章是原创,那些文章非原创还没有标记
把公众号文章转存到本地. 方便以后阅读.
公众号文章转存到本地的效果图
友情提示: 所有的抓包操作,请用自己的微信小号来操作,我不知道官方会不会有封号操作,反正小心使得成年船!
分页的时候数据出现了重复
我发现这里返回的数据有一个next_offset这个字段,看意思就是想下一页的数据偏移量,于是我把数据每次取出来之后,重新赋值,发现数据果然没有再次重复了.
self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br />
什么时候爬取完了数据,根本不知道
我在Charles中不断抓取数据,发现在抓取到尾页的时候,发现 can_msg_continue 状态变为0了,于是有了这一行代码
if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br />
那些文章是原创,那些文章非原创还没有标记
很多时候我比较喜欢原创文章,也深深尊重这些原创工作者,他们的文章都花费了大量心血,公众号正好有原创标记,所以我猜应该有原创字段标识, 我通过Charles抓包分析, 发现 copyright_stat 这个字段与版权意思比较接近,然后再进一步分析,发现这个状态等于11的时候,是为原创
copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br />
完整的抓取数据代码就是这些:
def parse_data(self, response_data):<br /><br /> all_datas = json.loads(response_data)<br /><br /> if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br /> summy_datas = all_datas['general_msg_list']<br /> datas = json.loads(summy_datas)['list']<br /> for data in datas:<br /> try:<br /> title = data['app_msg_ext_info']['title']<br /> title_child = data['app_msg_ext_info']['digest']<br /> article_url = data['app_msg_ext_info']['content_url']<br /> cover = data['app_msg_ext_info']['cover']<br /> copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br /> self.count = self.count + 1<br /> print('第【{}】篇文章'.format(self.count), copyright, title, title_child, article_url, cover)<br /> self.creat_pdf_file(article_url, '{}_{}'.format(copyright, title))<br /> except:<br /> continue<br /><br /> time.sleep(3)<br /> self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br /> else:<br /> if 0 == all_datas['can_msg_continue']:<br /> exit('数据抓取完毕!')<br /> else:<br /> exit('数据抓取出错:' + all_datas['errmsg'])<br />
把公众号文章转存到本地. 方便以后阅读.
仅仅把文章抓取到完全不能满足我的兴趣,我想把有价值的公众号文章放到本地自己来查看一下,于是我就有了我把文章转成pdf 文档的想法,怎么把文章转成pdf文档呢,
分下面三步操作:
self.config = pdfkit.configuration(wkhtmltopdf='C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe') # 这里需要配置一下wkhtmlpdf.exe路径<br /><br /> def creat_pdf_file(self, url, title):<br /> try:<br /> file = 'D:/store/file/{}.pdf'.format(title)<br /> if not os.path.exists(file): # 过滤掉重复文件<br /> pdfkit.from_url(url, file, configuration=self.config)<br /><br /> except Exception as e:<br /> print(e)<br />
效果图如下:
缺点还是有的,网页中的图片无法写入在pdf文件中,另外这个pdfkit用法还有很多,这里我就不深入了,有兴趣的朋友可以自行深入!
文章源码完整代码,由于篇幅有限,请后台回复 公众号抓取文章源码
下一篇,我们去抓取公众号里面的文中 阅读量,点赞量,这对于判断一篇文章是否优质有重要参考意义,另外还要抓取评论,生成词云,我们看一篇热文有那些观点,请持续关注! 查看全部
Python抓取公众号文章并生成pdf文件保存到本地
前面一篇文章由于做的时间比较仓促还留下了几个问题:
分页的时候出现了数据重复,
什么时候爬取完了数据,根本不知道
那些文章是原创,那些文章非原创还没有标记
把公众号文章转存到本地. 方便以后阅读.
公众号文章转存到本地的效果图
友情提示: 所有的抓包操作,请用自己的微信小号来操作,我不知道官方会不会有封号操作,反正小心使得成年船!
分页的时候数据出现了重复
我发现这里返回的数据有一个next_offset这个字段,看意思就是想下一页的数据偏移量,于是我把数据每次取出来之后,重新赋值,发现数据果然没有再次重复了.
self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br />
什么时候爬取完了数据,根本不知道
我在Charles中不断抓取数据,发现在抓取到尾页的时候,发现 can_msg_continue 状态变为0了,于是有了这一行代码
if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br />
那些文章是原创,那些文章非原创还没有标记
很多时候我比较喜欢原创文章,也深深尊重这些原创工作者,他们的文章都花费了大量心血,公众号正好有原创标记,所以我猜应该有原创字段标识, 我通过Charles抓包分析, 发现 copyright_stat 这个字段与版权意思比较接近,然后再进一步分析,发现这个状态等于11的时候,是为原创
copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br />
完整的抓取数据代码就是这些:
def parse_data(self, response_data):<br /><br /> all_datas = json.loads(response_data)<br /><br /> if 0 == all_datas['ret'] and 1 == all_datas['can_msg_continue']:<br /> summy_datas = all_datas['general_msg_list']<br /> datas = json.loads(summy_datas)['list']<br /> for data in datas:<br /> try:<br /> title = data['app_msg_ext_info']['title']<br /> title_child = data['app_msg_ext_info']['digest']<br /> article_url = data['app_msg_ext_info']['content_url']<br /> cover = data['app_msg_ext_info']['cover']<br /> copyright = data['app_msg_ext_info']['copyright_stat']<br /> copyright = '原创文章_' if copyright == 11 else '非原创文章_'<br /> self.count = self.count + 1<br /> print('第【{}】篇文章'.format(self.count), copyright, title, title_child, article_url, cover)<br /> self.creat_pdf_file(article_url, '{}_{}'.format(copyright, title))<br /> except:<br /> continue<br /><br /> time.sleep(3)<br /> self.offset = all_datas['next_offset'] # 下一页的偏移量<br /> self.request_data()<br /> else:<br /> if 0 == all_datas['can_msg_continue']:<br /> exit('数据抓取完毕!')<br /> else:<br /> exit('数据抓取出错:' + all_datas['errmsg'])<br />
把公众号文章转存到本地. 方便以后阅读.
仅仅把文章抓取到完全不能满足我的兴趣,我想把有价值的公众号文章放到本地自己来查看一下,于是我就有了我把文章转成pdf 文档的想法,怎么把文章转成pdf文档呢,
分下面三步操作:
self.config = pdfkit.configuration(wkhtmltopdf='C:/Program Files/wkhtmltopdf/bin/wkhtmltopdf.exe') # 这里需要配置一下wkhtmlpdf.exe路径<br /><br /> def creat_pdf_file(self, url, title):<br /> try:<br /> file = 'D:/store/file/{}.pdf'.format(title)<br /> if not os.path.exists(file): # 过滤掉重复文件<br /> pdfkit.from_url(url, file, configuration=self.config)<br /><br /> except Exception as e:<br /> print(e)<br />
效果图如下:
缺点还是有的,网页中的图片无法写入在pdf文件中,另外这个pdfkit用法还有很多,这里我就不深入了,有兴趣的朋友可以自行深入!
文章源码完整代码,由于篇幅有限,请后台回复 公众号抓取文章源码
下一篇,我们去抓取公众号里面的文中 阅读量,点赞量,这对于判断一篇文章是否优质有重要参考意义,另外还要抓取评论,生成词云,我们看一篇热文有那些观点,请持续关注!
scrapy分页抓取网页 6000 多款 App,看我如何搞定她们并将其洗白白~
网站优化 • 优采云 发表了文章 • 0 个评论 • 56 次浏览 • 2022-05-09 21:33
项目文件创建好以后,我们就可以开始写爬虫程序了。
首先,需要在 items.py 文件中,预先定义好要爬取的字段信息名称,如下所示:
1class KuanItem(scrapy.Item):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2# define the fields for your item here like:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3name = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4volume = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5download = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6follow = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7comment = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8tags = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10num_score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的字段信息就是我们前面在网页中定位的 8 个字段信息,包括:name 表示 App 名称、volume 表示体积、download 表示下载数量。在这里定义好之后,我们在后续的爬取主程序中会利用到这些字段信息。
2.3.3. 爬取主程序
创建好 kuan 项目后,Scrapy 框架会自动生成爬取的部分代码,我们接下来就需要在 parse 方法中增加网页抓取的字段解析内容。
1class KuanspiderSpider(scrapy.Spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 name = 'kuan'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 allowed_domains = ['www.coolapk.com']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 start_urls = ['http://www.coolapk.com/']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 pass<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
打开主页 Dev Tools,找到每项抓取指标的节点位置,然后可以采用 CSS、Xpath、正则等方法进行提取解析,这些方法 Scrapy 都支持,可随意选择,这里我们选用 CSS 语法来定位节点,不过需要注意的是,Scrapy 的 CSS 语法和之前我们利用 pyquery 使用的 CSS 语法稍有不同,举几个例子,对比说明一下。
首先,我们定位到第一个 APP 的主页 URL 节点,可以看到 URL 节点位于 class 属性为app_left_list的 div 节点下的 a 节点中,其 href 属性就是我们需要的 URL 信息,这里是相对地址,拼接后就是完整的 URL。
接着我们进入酷安详情页,选择 App 名称并进行定位,可以看到 App 名称节点位于 class 属性为.detail_app_title的 p 节点的文本中。
定位到这两个节点之后,我们就可以使用 CSS 提取字段信息了,这里对比一下常规写法和 Scrapy 中的写法:
1# 常规写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2url = item('.app_left_list>a').attr('href')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3name = item('.list_app_title').text()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4# Scrapy 写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5url = item.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6name = item.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
可以看到,要获取 href 或者 text 属性,需要用 :: 表示,比如获取 text,则用 ::text。extract_first() 表示提取第一个元素,如果有多个元素,则用 extract() 。接着,我们就可以参照写出 8 个字段信息的解析代码。
首先,我们需要在主页提取 App 的 URL 列表,然后再进入每个 App 的详情页进一步提取 8 个字段信息。
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = content.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 url = response.urljoin(url) # 拼接相对 url 为绝对 url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 yield scrapy.Request(url,callback=self.parse_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,利用 response.urljoin() 方法将提取出的相对 URL 拼接为完整的 URL,然后利用 scrapy.Request() 方法构造每个 App 详情页的请求,这里我们传递两个参数:url 和 callback,url 为详情页 URL,callback 是回调函数,它将主页 URL 请求返回的响应 response 传给专门用来解析字段内容的 parse_url() 方法,如下所示:
1def parse_url(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 item = KuanItem()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 item['name'] = response.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 results = self.get_comment(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 item['download'] = results[1]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 item['follow'] = results[2]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 item['comment'] = results[3]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 item['tags'] = self.get_tags(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 item['score'] = response.css('.rank_num::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 num_score = response.css('.apk_rank_p1::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 item['num_score'] = re.search('共(.*?)个评分',num_score).group(1)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 yield item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15def get_comment(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 messages = response.css('.apk_topba_message::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages) # \s+ 表示匹配任意空白字符一次以上<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 if result: # 不为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 results = list(result[0]) # 提取出list 中第一个元素<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 return results<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22def get_tags(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 data = response.css('.apk_left_span2')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24 tags = [item.css('::text').extract_first() for item in data]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25 return tags<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,单独定义了 get_comment() 和 get_tags() 两个方法.
get_comment() 方法通过正则匹配提取 volume、download、follow、comment 四个字段信息,正则匹配结果如下:
1result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(result) # 输出第一页的结果信息<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3# 结果如下:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4[('21.74M', '5218万', '2.4万', '5.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5[('75.53M', '2768万', '2.3万', '3.0万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6[('46.21M', '1686万', '2.3万', '3.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7[('54.77M', '1603万', '3.8万', '4.9万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8[('3.32M', '1530万', '1.5万', '3343')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9[('75.07M', '1127万', '1.6万', '2.2万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10[('92.70M', '1108万', '9167', '1.3万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11[('68.94M', '1072万', '5718', '9869')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12[('61.45M', '935万', '1.1万', '1.6万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13[('23.96M', '925万', '4157', '1956')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
然后利用 result[0]、result[1] 等分别提取出四项信息,以 volume 为例,输出第一页的提取结果:
1item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(item['volume'])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 321.74M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 475.53M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 546.21M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 654.77M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 73.32M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 875.07M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 992.70M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1068.94M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1161.45M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1223.96M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这样一来,第一页 10 款 App 的所有字段信息都被成功提取出来,然后返回到 yied item 生成器中,我们输出一下它的内容:
1[<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2{'name': '酷安', 'volume': '21.74M', 'download': '5218万', 'follow': '2.4万', 'comment': '5.4万', 'tags': "['酷市场', '酷安', '市场', 'coolapk', '装机必备']", 'score': '4.4', 'num_score': '1.4万'}, <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3{'name': '微信', 'volume': '75.53M', 'download': '2768万', 'follow': '2.3万', 'comment': '3.0万', 'tags': "['微信', 'qq', '腾讯', 'tencent', '即时聊天', '装机必备']",'score': '2.3', 'num_score': '1.1万'},<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
2.3.4. 分页爬取
以上,我们爬取了第一页内容,接下去需要遍历爬取全部 610 页的内容,这里有两种思路:
这里,我们分别写出两种方法的解析代码,第一种方法很简单,直接接着 parse 方法继续添加以下几行代码即可:
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 ...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 next_page = response.css('.pagination li:nth-child(8) a::attr(href)').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 url = response.urljoin(next_page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />8 yield scrapy.Request(url,callback=self.parse )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二种方法,我们在最开头的 parse() 方法前,定义一个 start_requests() 方法,用来批量生成 610 页的 URL,然后通过 scrapy.Request() 方法中的 callback 参数,传递给下面的 parse() 方法进行解析。
1def start_requests(self):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 pages = []<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for page in range(1,610): # 一共有610页<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = 'https://www.coolapk.com/apk/?page=%s'%page<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 page = scrapy.Request(url,callback=self.parse)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 pages.append(page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 return pages<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上就是全部页面的爬取思路,爬取成功后,我们需要存储下来。这里,我面选择存储到 MongoDB 中,不得不说,相比 MySQL,MongoDB 要方便省事很多。
2.3.5. 存储结果
我们在 pipelines.py 程序中,定义数据存储方法,MongoDB 的一些参数,比如地址和数据库名称,需单独存放在 settings.py 设置文件中去,然后在 pipelines 程序中进行调用即可。
1import pymongo<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2class MongoPipeline(object):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 def __init__(self,mongo_url,mongo_db):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 self.mongo_url = mongo_url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 self.mongo_db = mongo_db<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 @classmethod<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 def from_crawler(cls,crawler):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 return cls(<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 mongo_url = crawler.settings.get('MONGO_URL'),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 mongo_db = crawler.settings.get('MONGO_DB')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 def open_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 self.client = pymongo.MongoClient(self.mongo_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 self.db = self.client[self.mongo_db]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 def process_item(self,item,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 name = item.__class__.__name__<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 self.db[name].insert(dict(item))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 return item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 def close_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 self.client.close()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
首先,我们定义一个 MongoPipeline()存储类,里面定义了几个方法,简单进行一下说明:
from crawler() 是一个类方法,用 @class method 标识,这个方法的作用主要是用来获取我们在 settings.py 中设置的这几项参数:
1MONGO_URL = 'localhost'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2MONGO_DB = 'KuAn'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3ITEM_PIPELINES = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 'kuan.pipelines.MongoPipeline': 300,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
open_spider() 方法主要进行一些初始化操作 ,在 Spider 开启时,这个方法就会被调用 。
process_item() 方法是最重要的方法,实现插入数据到 MongoDB 中。
完成上述代码以后,输入下面一行命令就可以开始整个爬虫的抓取和存储过程了,单机跑的话,6000 个网页需要不少时间才能完成,保持耐心。
1scrapy crawl kuan<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,还有两点补充:
第一,为了减轻网站压力,我们最好在每个请求之间设置几秒延时,可以在 KuanSpider() 方法开头出,加入以下几行代码:
1custom_settings = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 "DOWNLOAD_DELAY": 3, # 延迟3s,默认是0,即不延迟<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 "CONCURRENT_REQUESTS_PER_DOMAIN": 8 # 每秒默认并发8次,可适当降低<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二,为了更好监控爬虫程序运行,有必要设置输出日志文件,可以通过 Python 自带的 logging 包实现:
1import logging<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3logging.basicConfig(filename='kuan.log',filemode='w',level=logging.WARNING,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4logging.warning("warn message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5logging.error("error message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的 level 参数表示警告级别,严重程度从低到高分别是:DEBUG < INFO < WARNING < ERROR < CRITICAL,如果想日志文件不要记录太多内容,可以设置高一点的级别,这里设置为 WARNING,意味着只有 WARNING 级别以上的信息才会输出到日志中去。
添加 datefmt 参数是为了在每条日志前面加具体的时间,这点很有用处。
以上,我们就完成了整个数据的抓取,有了数据我们就可以着手进行分析,不过这之前还需简单地对数据做一下清洗和处理。
3. 数据清洗处理
首先,我们从 MongoDB 中读取数据并转化为 DataFrame,然后查看一下数据的基本情况。
1def parse_kuan():<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 client = pymongo.MongoClient(host='localhost', port=27017)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 db = client['KuAn']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 collection = db['KuAnItem']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 # 将数据库数据转为DataFrame<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 data = pd.DataFrame(list(collection.find()))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 print(data.head())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 print(df.shape)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 print(df.info())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 print(df.describe())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
从 data.head() 输出的前 5 行数据中可以看到,除了 score 列是 float 格式以外,其他列都是 object 文本类型。
comment、download、follow、num_score 这 5 列数据中部分行带有「万」字后缀,需要将字符去掉再转换为数值型;volume 体积列,则分别带有「M」和「K」后缀,为了统一大小,则需将「K」除以 1024,转换为 「M」体积。
整个数据一共有 6086 行 x 8 列,每列均没有缺失值。
df.describe() 方法对 score 列做了基本统计,可以看到,所有 App 的平均得分是 3.9 分(5 分制),最低得分 1.6 分,最高得分 4.8 分。
下面,我们将以上几列文本型数据转换为数值型数据,代码实现如下:
1def data_processing(df):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2#处理'comment','download','follow','num_score','volume' 5列数据,将单位万转换为单位1,再转换为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 str = '_ori'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 cols = ['comment','download','follow','num_score','volume']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 for col in cols:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 colori = col+str<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 df[colori] = df[col] # 复制保留原始列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 if not (col == 'volume'):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 df[col] = clean_symbol(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 else:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 df[col] = clean_symbol2(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 # 将download单独转换为万单位<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 df['download'] = df['download'].apply(lambda x:x/10000)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 # 批量转为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 df = df.apply(pd.to_numeric,errors='ignore')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18def clean_symbol(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 # 将字符“万”替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 con = df[col].str.contains('万$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('万','')) * 10000<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25def clean_symbol2(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />26 # 字符M替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />27 df[col] = df[col].str.replace('M$','')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />28 # 体积为K的除以 1024 转换为M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />29 con = df[col].str.contains('K$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />30 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('K$',''))/1024<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />31 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />32 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上,就完成了几列文本型数据的转换,我们再来查看一下基本情况:
download 列为 App 下载数量,下载量最多的 App 有 5190 万次,最少的为 0 (很少很少),平均下载次数为 14 万次;从中可以看出以下几点信息:
以上,就完成了基本的数据清洗处理过程,下一期将对这6000多款App进行探索性分析,看看有多少佳软神器你没有使用过哦。 查看全部
scrapy分页抓取网页 6000 多款 App,看我如何搞定她们并将其洗白白~
项目文件创建好以后,我们就可以开始写爬虫程序了。
首先,需要在 items.py 文件中,预先定义好要爬取的字段信息名称,如下所示:
1class KuanItem(scrapy.Item):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2# define the fields for your item here like:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3name = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4volume = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5download = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6follow = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7comment = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8tags = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10num_score = scrapy.Field()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的字段信息就是我们前面在网页中定位的 8 个字段信息,包括:name 表示 App 名称、volume 表示体积、download 表示下载数量。在这里定义好之后,我们在后续的爬取主程序中会利用到这些字段信息。
2.3.3. 爬取主程序
创建好 kuan 项目后,Scrapy 框架会自动生成爬取的部分代码,我们接下来就需要在 parse 方法中增加网页抓取的字段解析内容。
1class KuanspiderSpider(scrapy.Spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 name = 'kuan'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 allowed_domains = ['www.coolapk.com']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 start_urls = ['http://www.coolapk.com/']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 pass<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
打开主页 Dev Tools,找到每项抓取指标的节点位置,然后可以采用 CSS、Xpath、正则等方法进行提取解析,这些方法 Scrapy 都支持,可随意选择,这里我们选用 CSS 语法来定位节点,不过需要注意的是,Scrapy 的 CSS 语法和之前我们利用 pyquery 使用的 CSS 语法稍有不同,举几个例子,对比说明一下。
首先,我们定位到第一个 APP 的主页 URL 节点,可以看到 URL 节点位于 class 属性为app_left_list的 div 节点下的 a 节点中,其 href 属性就是我们需要的 URL 信息,这里是相对地址,拼接后就是完整的 URL。
接着我们进入酷安详情页,选择 App 名称并进行定位,可以看到 App 名称节点位于 class 属性为.detail_app_title的 p 节点的文本中。
定位到这两个节点之后,我们就可以使用 CSS 提取字段信息了,这里对比一下常规写法和 Scrapy 中的写法:
1# 常规写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2url = item('.app_left_list>a').attr('href')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3name = item('.list_app_title').text()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4# Scrapy 写法<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5url = item.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6name = item.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
可以看到,要获取 href 或者 text 属性,需要用 :: 表示,比如获取 text,则用 ::text。extract_first() 表示提取第一个元素,如果有多个元素,则用 extract() 。接着,我们就可以参照写出 8 个字段信息的解析代码。
首先,我们需要在主页提取 App 的 URL 列表,然后再进入每个 App 的详情页进一步提取 8 个字段信息。
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = content.css('::attr("href")').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 url = response.urljoin(url) # 拼接相对 url 为绝对 url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 yield scrapy.Request(url,callback=self.parse_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,利用 response.urljoin() 方法将提取出的相对 URL 拼接为完整的 URL,然后利用 scrapy.Request() 方法构造每个 App 详情页的请求,这里我们传递两个参数:url 和 callback,url 为详情页 URL,callback 是回调函数,它将主页 URL 请求返回的响应 response 传给专门用来解析字段内容的 parse_url() 方法,如下所示:
1def parse_url(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 item = KuanItem()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 item['name'] = response.css('.detail_app_title::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 results = self.get_comment(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 item['download'] = results[1]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 item['follow'] = results[2]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 item['comment'] = results[3]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 item['tags'] = self.get_tags(response)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 item['score'] = response.css('.rank_num::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 num_score = response.css('.apk_rank_p1::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 item['num_score'] = re.search('共(.*?)个评分',num_score).group(1)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 yield item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15def get_comment(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 messages = response.css('.apk_topba_message::text').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages) # \s+ 表示匹配任意空白字符一次以上<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 if result: # 不为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 results = list(result[0]) # 提取出list 中第一个元素<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 return results<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22def get_tags(self,response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 data = response.css('.apk_left_span2')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24 tags = [item.css('::text').extract_first() for item in data]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25 return tags<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,单独定义了 get_comment() 和 get_tags() 两个方法.
get_comment() 方法通过正则匹配提取 volume、download、follow、comment 四个字段信息,正则匹配结果如下:
1result = re.findall(r'\s+(.*?)\s+/\s+(.*?)下载\s+/\s+(.*?)人关注\s+/\s+(.*?)个评论.*?',messages)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(result) # 输出第一页的结果信息<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3# 结果如下:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4[('21.74M', '5218万', '2.4万', '5.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5[('75.53M', '2768万', '2.3万', '3.0万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6[('46.21M', '1686万', '2.3万', '3.4万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7[('54.77M', '1603万', '3.8万', '4.9万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8[('3.32M', '1530万', '1.5万', '3343')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9[('75.07M', '1127万', '1.6万', '2.2万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10[('92.70M', '1108万', '9167', '1.3万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11[('68.94M', '1072万', '5718', '9869')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12[('61.45M', '935万', '1.1万', '1.6万')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13[('23.96M', '925万', '4157', '1956')]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
然后利用 result[0]、result[1] 等分别提取出四项信息,以 volume 为例,输出第一页的提取结果:
1item['volume'] = results[0]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2print(item['volume'])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 321.74M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 475.53M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 546.21M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 654.77M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 73.32M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 875.07M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 992.70M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1068.94M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1161.45M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />1223.96M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这样一来,第一页 10 款 App 的所有字段信息都被成功提取出来,然后返回到 yied item 生成器中,我们输出一下它的内容:
1[<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2{'name': '酷安', 'volume': '21.74M', 'download': '5218万', 'follow': '2.4万', 'comment': '5.4万', 'tags': "['酷市场', '酷安', '市场', 'coolapk', '装机必备']", 'score': '4.4', 'num_score': '1.4万'}, <br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3{'name': '微信', 'volume': '75.53M', 'download': '2768万', 'follow': '2.3万', 'comment': '3.0万', 'tags': "['微信', 'qq', '腾讯', 'tencent', '即时聊天', '装机必备']",'score': '2.3', 'num_score': '1.1万'},<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
2.3.4. 分页爬取
以上,我们爬取了第一页内容,接下去需要遍历爬取全部 610 页的内容,这里有两种思路:
这里,我们分别写出两种方法的解析代码,第一种方法很简单,直接接着 parse 方法继续添加以下几行代码即可:
1def parse(self, response):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 contents = response.css('.app_left_list>a')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for content in contents:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 ...<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 next_page = response.css('.pagination li:nth-child(8) a::attr(href)').extract_first()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 url = response.urljoin(next_page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />8 yield scrapy.Request(url,callback=self.parse )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二种方法,我们在最开头的 parse() 方法前,定义一个 start_requests() 方法,用来批量生成 610 页的 URL,然后通过 scrapy.Request() 方法中的 callback 参数,传递给下面的 parse() 方法进行解析。
1def start_requests(self):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 pages = []<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 for page in range(1,610): # 一共有610页<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 url = 'https://www.coolapk.com/apk/?page=%s'%page<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5 page = scrapy.Request(url,callback=self.parse)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />6 pages.append(page)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />7 return pages<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上就是全部页面的爬取思路,爬取成功后,我们需要存储下来。这里,我面选择存储到 MongoDB 中,不得不说,相比 MySQL,MongoDB 要方便省事很多。
2.3.5. 存储结果
我们在 pipelines.py 程序中,定义数据存储方法,MongoDB 的一些参数,比如地址和数据库名称,需单独存放在 settings.py 设置文件中去,然后在 pipelines 程序中进行调用即可。
1import pymongo<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2class MongoPipeline(object):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 def __init__(self,mongo_url,mongo_db):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 self.mongo_url = mongo_url<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 self.mongo_db = mongo_db<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 @classmethod<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 def from_crawler(cls,crawler):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 return cls(<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 mongo_url = crawler.settings.get('MONGO_URL'),<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 mongo_db = crawler.settings.get('MONGO_DB')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 )<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12 def open_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 self.client = pymongo.MongoClient(self.mongo_url)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 self.db = self.client[self.mongo_db]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 def process_item(self,item,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 name = item.__class__.__name__<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17 self.db[name].insert(dict(item))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18 return item<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 def close_spider(self,spider):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 self.client.close()<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
首先,我们定义一个 MongoPipeline()存储类,里面定义了几个方法,简单进行一下说明:
from crawler() 是一个类方法,用 @class method 标识,这个方法的作用主要是用来获取我们在 settings.py 中设置的这几项参数:
1MONGO_URL = 'localhost'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2MONGO_DB = 'KuAn'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3ITEM_PIPELINES = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 'kuan.pipelines.MongoPipeline': 300,<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
open_spider() 方法主要进行一些初始化操作 ,在 Spider 开启时,这个方法就会被调用 。
process_item() 方法是最重要的方法,实现插入数据到 MongoDB 中。
完成上述代码以后,输入下面一行命令就可以开始整个爬虫的抓取和存储过程了,单机跑的话,6000 个网页需要不少时间才能完成,保持耐心。
1scrapy crawl kuan<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里,还有两点补充:
第一,为了减轻网站压力,我们最好在每个请求之间设置几秒延时,可以在 KuanSpider() 方法开头出,加入以下几行代码:
1custom_settings = {<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2 "DOWNLOAD_DELAY": 3, # 延迟3s,默认是0,即不延迟<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3 "CONCURRENT_REQUESTS_PER_DOMAIN": 8 # 每秒默认并发8次,可适当降低<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4 }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
第二,为了更好监控爬虫程序运行,有必要设置输出日志文件,可以通过 Python 自带的 logging 包实现:
1import logging<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />2<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />3logging.basicConfig(filename='kuan.log',filemode='w',level=logging.WARNING,format='%(asctime)s %(message)s',datefmt='%Y/%m/%d %I:%M:%S %p')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />4logging.warning("warn message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />5logging.error("error message")<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
这里的 level 参数表示警告级别,严重程度从低到高分别是:DEBUG < INFO < WARNING < ERROR < CRITICAL,如果想日志文件不要记录太多内容,可以设置高一点的级别,这里设置为 WARNING,意味着只有 WARNING 级别以上的信息才会输出到日志中去。
添加 datefmt 参数是为了在每条日志前面加具体的时间,这点很有用处。
以上,我们就完成了整个数据的抓取,有了数据我们就可以着手进行分析,不过这之前还需简单地对数据做一下清洗和处理。
3. 数据清洗处理
首先,我们从 MongoDB 中读取数据并转化为 DataFrame,然后查看一下数据的基本情况。
1def parse_kuan():<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2 client = pymongo.MongoClient(host='localhost', port=27017)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 db = client['KuAn']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 collection = db['KuAnItem']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 # 将数据库数据转为DataFrame<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 data = pd.DataFrame(list(collection.find()))<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 print(data.head())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 print(df.shape)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 print(df.info())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 print(df.describe())<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
从 data.head() 输出的前 5 行数据中可以看到,除了 score 列是 float 格式以外,其他列都是 object 文本类型。
comment、download、follow、num_score 这 5 列数据中部分行带有「万」字后缀,需要将字符去掉再转换为数值型;volume 体积列,则分别带有「M」和「K」后缀,为了统一大小,则需将「K」除以 1024,转换为 「M」体积。
整个数据一共有 6086 行 x 8 列,每列均没有缺失值。
df.describe() 方法对 score 列做了基本统计,可以看到,所有 App 的平均得分是 3.9 分(5 分制),最低得分 1.6 分,最高得分 4.8 分。
下面,我们将以上几列文本型数据转换为数值型数据,代码实现如下:
1def data_processing(df):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 2#处理'comment','download','follow','num_score','volume' 5列数据,将单位万转换为单位1,再转换为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 3 str = '_ori'<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 4 cols = ['comment','download','follow','num_score','volume']<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 5 for col in cols:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 6 colori = col+str<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 7 df[colori] = df[col] # 复制保留原始列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 8 if not (col == 'volume'):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" /> 9 df[col] = clean_symbol(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />10 else:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />11 df[col] = clean_symbol2(df,col)# 处理原始列生成新列<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />12<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />13 # 将download单独转换为万单位<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />14 df['download'] = df['download'].apply(lambda x:x/10000)<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />15 # 批量转为数值型<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />16 df = df.apply(pd.to_numeric,errors='ignore')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />17<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />18def clean_symbol(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />19 # 将字符“万”替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />20 con = df[col].str.contains('万$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />21 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('万','')) * 10000<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />22 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />23 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />24<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />25def clean_symbol2(df,col):<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />26 # 字符M替换为空<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />27 df[col] = df[col].str.replace('M$','')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />28 # 体积为K的除以 1024 转换为M<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />29 con = df[col].str.contains('K$')<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />30 df.loc[con,col] = pd.to_numeric(df.loc[con,col].str.replace('K$',''))/1024<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />31 df[col] = pd.to_numeric(df[col])<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />32 return df[col]<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;" />
以上,就完成了几列文本型数据的转换,我们再来查看一下基本情况:
download 列为 App 下载数量,下载量最多的 App 有 5190 万次,最少的为 0 (很少很少),平均下载次数为 14 万次;从中可以看出以下几点信息:
以上,就完成了基本的数据清洗处理过程,下一期将对这6000多款App进行探索性分析,看看有多少佳软神器你没有使用过哦。
爬虫 | 如何构建技术文章聚合平台(一)
网站优化 • 优采云 发表了文章 • 0 个评论 • 78 次浏览 • 2022-05-08 00:10
博客地址:
背景
说到爬虫,大多数程序员想到的是scrapy这样受人欢迎的框架。scrapy的确不错,而且有很强大的生态圈,有gerapy等优秀的可视化界面。但是,它还是有一些不能做到的事情,例如在页面上做翻页点击操作、移动端抓取等等。对于这些新的需求,可以用Selenium、Puppeteer、Appium这些自动化测试框架绕开繁琐的动态内容,直接模拟用户操作进行抓取。可惜的是,这些框架不是专门的爬虫框架,不能对爬虫进行集中管理,因此对于一个多达数十个爬虫的大型项目来说有些棘手。
Crawlab是一个基于Celery的分布式通用爬虫管理平台,擅长将不同编程语言编写的爬虫整合在一处,方便监控和管理。Crawlab有精美的可视化界面,能对多个爬虫进行运行和管理。任务调度引擎是本身支持分布式架构的Celery,因此Crawlab可以天然集成分布式爬虫。有一些朋友认为Crawlab只是一个任务调度引擎,其实这样认为并不完全正确。Crawlab是类似Gerapy这样的专注于爬虫的管理平台。
本文将介绍如何使用Crawlab和Puppeteer抓取主流的技术博客文章,然后用Flask+Vue搭建一个小型的技术文章聚合平台。
Crawlab
在前一篇文章《分布式通用爬虫管理平台Crawlab》已介绍了Crawlab的架构以及安装使用,这里快速介绍一下如何安装、运行、使用Crawlab。(感兴趣的同学可以去作者的掘金主页查看)
安装
到Crawlab的Github Repo用克隆一份到本地。
<p>git clone https://github.com/tikazyq/crawlab
复制代码</p>
安装相应的依赖包和库。
<p>cd crawlab
<br />
# 安装python依赖
pip install -r crawlab/requirements
<br />
# 安装前端依赖
cd frontend
npm install
复制代码</p>
安装mongodb和redis-server。Crawlab将用MongoDB作为结果集以及运行操作的储存方式,Redis作为Celery的任务队列,因此需要安装这两个数据库。
运行
在运行之前需要对Crawlab进行一些配置,配置文件为config.py。
<p># project variables
PROJECT_SOURCE_FILE_FOLDER = '/Users/yeqing/projects/crawlab/spiders' # 爬虫源码根目录
PROJECT_DEPLOY_FILE_FOLDER = '/var/crawlab' # 爬虫部署根目录
PROJECT_LOGS_FOLDER = '/var/logs/crawlab' # 日志目录
PROJECT_TMP_FOLDER = '/tmp' # 临时文件目录
<br />
# celery variables
BROKER_URL = 'redis://192.168.99.100:6379/0' # 中间者URL,连接redis
CELERY_RESULT_BACKEND = 'mongodb://192.168.99.100:27017/' # CELERY后台URL
CELERY_MONGODB_BACKEND_SETTINGS = {
'database': 'crawlab_test',
'taskmeta_collection': 'tasks_celery',
}
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
<br />
# flower variables
FLOWER_API_ENDPOINT = 'http://localhost:5555/api' # Flower服务地址
<br />
# database variables
MONGO_HOST = '192.168.99.100'
MONGO_PORT = 27017
MONGO_DB = 'crawlab_test'
<br />
# flask variables
DEBUG = True
FLASK_HOST = '127.0.0.1'
FLASK_PORT = 8000
复制代码</p>
启动后端API,也就是一个Flask App,可以直接启动,或者用gunicorn代替。
<p>cd ../crawlab
python app.py
复制代码</p>
启动Flower服务(抱歉目前集成Flower到App服务中,必须单独启动来获取节点信息,后面的版本不需要这个操作)。
<p>python ./bin/run_flower.py
复制代码</p>
启动本地Worker。在其他节点中如果想只是想执行任务的话,只需要启动这一个服务就可以了。
<p>python ./bin/run_worker.py
复制代码</p>
启动前端服务器。
<p>cd ../frontend
npm run serve
复制代码</p>
使用
首页Home中可以看到总任务数、总爬虫数、在线节点数和总部署数,以及过去30天的任务运行数量。
点击侧边栏的Spiders或者上方到Spiders数,可以进入到爬虫列表页。
这些是爬虫源码根目录PROJECT_SOURCE_FILE_FOLDER下的爬虫。Crawlab会自动扫描该目录下的子目录,将子目录看作一个爬虫。Action列下有一些操作选项,点击部署Deploy按钮将爬虫部署到所有在线节点中。部署成功后,点击运行Run按钮,触发抓取任务。这时,任务应该已经在执行了。点击侧边栏的Tasks到任务列表,可以看到已经调度过的爬虫任务。
基本使用就是这些,但是Crawlab还能做到更多,大家可以进一步探索,详情请见Github。
Puppeteer
Puppeteer是谷歌开源的基于Chromium和NodeJS的自动化测试工具,可以很方便的让程序模拟用户的操作,对浏览器进行程序化控制。Puppeteer有一些常用操作,例如点击,鼠标移动,滑动,截屏,下载文件等等。另外,Puppeteer很类似Selenium,可以定位浏览器中网页元素,将其数据抓取下来。因此,Puppeteer也成为了新的爬虫利器。
相对于Selenium,Puppeteer是新的开源项目,而且是谷歌开发,可以使用很多新的特性。对于爬虫来说,如果前端知识足够的话,写数据抓取逻辑简直不能再简单。正如其名字一样,我们是在操作木偶人来帮我们抓取数据,是不是很贴切?
掘金上已经有很多关于Puppeteer的教程了(爬虫利器 Puppeteer 实战、Puppeteer 与 Chrome Headless —— 从入门到爬虫),这里只简单介绍一下Puppeteer的安装和使用。
安装
安装很简单,就一行npm install命令,npm会自动下载Chromium并安装,这个时间会比较长。为了让安装好的puppeteer模块能够被所有nodejs爬虫所共享,我们在PROJECT_DEPLOY_FILE_FOLDER目录下安装node的包。
<p># PROJECT_DEPLOY_FILE_FOLDER变量值
cd /var/crawlab
<br />
# 安装puppeteer
npm i puppeteer
<br />
# 安装mongodb
npm i mongodb
复制代码</p>
安装mongodb是为了后续的数据库操作。
使用
以下是Copy/Paste的一段用Puppeteer访问简书然后截屏的代码,非常简洁。
<p>const puppeteer = require('puppeteer');
<br />
(async () => {
const browser = await (puppeteer.launch());
const page = await browser.newPage();
await page.goto('https://www.jianshu.com/u/40909ea33e50');
await page.screenshot({
path: 'jianshu.png',
type: 'png',
// quality: 100, 只对jpg有效
fullPage: true,
// 指定区域截图,clip和fullPage两者只能设置一个
// clip: {
// x: 0,
// y: 0,
// width: 1000,
// height: 40
// }
});
browser.close();
})();
复制代码</p>
关于Puppeteer的常用操作,请移步《我常用的puppeteer爬虫api》。
编写爬虫
啰嗦了这么久,终于到了万众期待的爬虫时间了。Talk is cheap, show me the code!咦?我们不是已经Show了不少代码了么...
由于我们的目标是建立一个技术文章聚合平台,我们需要去各大技术网站抓取文章。资源当然是越多越好。作为展示用,我们将抓取下面几个具有代表性的网站:
研究发现这三个网站都是由Ajax获取文章列表,生成动态内容以作为传统的分页替代。这对于Puppeteer来说很容易处理,因为Puppeteer绕开了解析Ajax这一部分,浏览器会自动处理这样的操作和请求,我们只着重关注数据获取就行了。三个网站的抓取策略基本相同,我们以掘金为例着重讲解。
掘金
首先是引入Puppeteer和打开网页。
<p>const puppeteer = require('puppeteer');
const MongoClient = require('mongodb').MongoClient;
<br />
(async () => {
// browser
const browser = await (puppeteer.launch({
headless: true
}));
<br />
// define start url
const url = 'https://juejin.im';
<br />
// start a new page
const page = await browser.newPage();
<br />
...
<br />
})();
复制代码</p>
headless设置为true可以让浏览器以headless的方式运行,也就是指浏览器不用在界面中打开,它会在后台运行,用户是看不到浏览器的。browser.newPage()将新生成一个标签页。后面的操作基本就围绕着生成的page来进行。
接下来我们让浏览器导航到start url。
<p> ...
<br />
// navigate to url
try {
await page.goto(url, {waitUntil: 'domcontentloaded'});
await page.waitFor(2000);
} catch (e) {
console.error(e);
<br />
// close browser
browser.close();
<br />
// exit code 1 indicating an error happened
code = 1;
process.emit("exit ");
process.reallyExit(code);
<br />
return
}
<br />
...
复制代码</p>
这里trycatch的操作是为了处理浏览器访问超时的错误。当访问超时时,设置exit code为1表示该任务失败了,这样Crawlab会将该任务状态设置为FAILURE。 查看全部
爬虫 | 如何构建技术文章聚合平台(一)
博客地址:
背景
说到爬虫,大多数程序员想到的是scrapy这样受人欢迎的框架。scrapy的确不错,而且有很强大的生态圈,有gerapy等优秀的可视化界面。但是,它还是有一些不能做到的事情,例如在页面上做翻页点击操作、移动端抓取等等。对于这些新的需求,可以用Selenium、Puppeteer、Appium这些自动化测试框架绕开繁琐的动态内容,直接模拟用户操作进行抓取。可惜的是,这些框架不是专门的爬虫框架,不能对爬虫进行集中管理,因此对于一个多达数十个爬虫的大型项目来说有些棘手。
Crawlab是一个基于Celery的分布式通用爬虫管理平台,擅长将不同编程语言编写的爬虫整合在一处,方便监控和管理。Crawlab有精美的可视化界面,能对多个爬虫进行运行和管理。任务调度引擎是本身支持分布式架构的Celery,因此Crawlab可以天然集成分布式爬虫。有一些朋友认为Crawlab只是一个任务调度引擎,其实这样认为并不完全正确。Crawlab是类似Gerapy这样的专注于爬虫的管理平台。
本文将介绍如何使用Crawlab和Puppeteer抓取主流的技术博客文章,然后用Flask+Vue搭建一个小型的技术文章聚合平台。
Crawlab
在前一篇文章《分布式通用爬虫管理平台Crawlab》已介绍了Crawlab的架构以及安装使用,这里快速介绍一下如何安装、运行、使用Crawlab。(感兴趣的同学可以去作者的掘金主页查看)
安装
到Crawlab的Github Repo用克隆一份到本地。
<p>git clone https://github.com/tikazyq/crawlab
复制代码</p>
安装相应的依赖包和库。
<p>cd crawlab
<br />
# 安装python依赖
pip install -r crawlab/requirements
<br />
# 安装前端依赖
cd frontend
npm install
复制代码</p>
安装mongodb和redis-server。Crawlab将用MongoDB作为结果集以及运行操作的储存方式,Redis作为Celery的任务队列,因此需要安装这两个数据库。
运行
在运行之前需要对Crawlab进行一些配置,配置文件为config.py。
<p># project variables
PROJECT_SOURCE_FILE_FOLDER = '/Users/yeqing/projects/crawlab/spiders' # 爬虫源码根目录
PROJECT_DEPLOY_FILE_FOLDER = '/var/crawlab' # 爬虫部署根目录
PROJECT_LOGS_FOLDER = '/var/logs/crawlab' # 日志目录
PROJECT_TMP_FOLDER = '/tmp' # 临时文件目录
<br />
# celery variables
BROKER_URL = 'redis://192.168.99.100:6379/0' # 中间者URL,连接redis
CELERY_RESULT_BACKEND = 'mongodb://192.168.99.100:27017/' # CELERY后台URL
CELERY_MONGODB_BACKEND_SETTINGS = {
'database': 'crawlab_test',
'taskmeta_collection': 'tasks_celery',
}
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERY_ENABLE_UTC = True
<br />
# flower variables
FLOWER_API_ENDPOINT = 'http://localhost:5555/api' # Flower服务地址
<br />
# database variables
MONGO_HOST = '192.168.99.100'
MONGO_PORT = 27017
MONGO_DB = 'crawlab_test'
<br />
# flask variables
DEBUG = True
FLASK_HOST = '127.0.0.1'
FLASK_PORT = 8000
复制代码</p>
启动后端API,也就是一个Flask App,可以直接启动,或者用gunicorn代替。
<p>cd ../crawlab
python app.py
复制代码</p>
启动Flower服务(抱歉目前集成Flower到App服务中,必须单独启动来获取节点信息,后面的版本不需要这个操作)。
<p>python ./bin/run_flower.py
复制代码</p>
启动本地Worker。在其他节点中如果想只是想执行任务的话,只需要启动这一个服务就可以了。
<p>python ./bin/run_worker.py
复制代码</p>
启动前端服务器。
<p>cd ../frontend
npm run serve
复制代码</p>
使用
首页Home中可以看到总任务数、总爬虫数、在线节点数和总部署数,以及过去30天的任务运行数量。
点击侧边栏的Spiders或者上方到Spiders数,可以进入到爬虫列表页。
这些是爬虫源码根目录PROJECT_SOURCE_FILE_FOLDER下的爬虫。Crawlab会自动扫描该目录下的子目录,将子目录看作一个爬虫。Action列下有一些操作选项,点击部署Deploy按钮将爬虫部署到所有在线节点中。部署成功后,点击运行Run按钮,触发抓取任务。这时,任务应该已经在执行了。点击侧边栏的Tasks到任务列表,可以看到已经调度过的爬虫任务。
基本使用就是这些,但是Crawlab还能做到更多,大家可以进一步探索,详情请见Github。
Puppeteer
Puppeteer是谷歌开源的基于Chromium和NodeJS的自动化测试工具,可以很方便的让程序模拟用户的操作,对浏览器进行程序化控制。Puppeteer有一些常用操作,例如点击,鼠标移动,滑动,截屏,下载文件等等。另外,Puppeteer很类似Selenium,可以定位浏览器中网页元素,将其数据抓取下来。因此,Puppeteer也成为了新的爬虫利器。
相对于Selenium,Puppeteer是新的开源项目,而且是谷歌开发,可以使用很多新的特性。对于爬虫来说,如果前端知识足够的话,写数据抓取逻辑简直不能再简单。正如其名字一样,我们是在操作木偶人来帮我们抓取数据,是不是很贴切?
掘金上已经有很多关于Puppeteer的教程了(爬虫利器 Puppeteer 实战、Puppeteer 与 Chrome Headless —— 从入门到爬虫),这里只简单介绍一下Puppeteer的安装和使用。
安装
安装很简单,就一行npm install命令,npm会自动下载Chromium并安装,这个时间会比较长。为了让安装好的puppeteer模块能够被所有nodejs爬虫所共享,我们在PROJECT_DEPLOY_FILE_FOLDER目录下安装node的包。
<p># PROJECT_DEPLOY_FILE_FOLDER变量值
cd /var/crawlab
<br />
# 安装puppeteer
npm i puppeteer
<br />
# 安装mongodb
npm i mongodb
复制代码</p>
安装mongodb是为了后续的数据库操作。
使用
以下是Copy/Paste的一段用Puppeteer访问简书然后截屏的代码,非常简洁。
<p>const puppeteer = require('puppeteer');
<br />
(async () => {
const browser = await (puppeteer.launch());
const page = await browser.newPage();
await page.goto('https://www.jianshu.com/u/40909ea33e50');
await page.screenshot({
path: 'jianshu.png',
type: 'png',
// quality: 100, 只对jpg有效
fullPage: true,
// 指定区域截图,clip和fullPage两者只能设置一个
// clip: {
// x: 0,
// y: 0,
// width: 1000,
// height: 40
// }
});
browser.close();
})();
复制代码</p>
关于Puppeteer的常用操作,请移步《我常用的puppeteer爬虫api》。
编写爬虫
啰嗦了这么久,终于到了万众期待的爬虫时间了。Talk is cheap, show me the code!咦?我们不是已经Show了不少代码了么...
由于我们的目标是建立一个技术文章聚合平台,我们需要去各大技术网站抓取文章。资源当然是越多越好。作为展示用,我们将抓取下面几个具有代表性的网站:
研究发现这三个网站都是由Ajax获取文章列表,生成动态内容以作为传统的分页替代。这对于Puppeteer来说很容易处理,因为Puppeteer绕开了解析Ajax这一部分,浏览器会自动处理这样的操作和请求,我们只着重关注数据获取就行了。三个网站的抓取策略基本相同,我们以掘金为例着重讲解。
掘金
首先是引入Puppeteer和打开网页。
<p>const puppeteer = require('puppeteer');
const MongoClient = require('mongodb').MongoClient;
<br />
(async () => {
// browser
const browser = await (puppeteer.launch({
headless: true
}));
<br />
// define start url
const url = 'https://juejin.im';
<br />
// start a new page
const page = await browser.newPage();
<br />
...
<br />
})();
复制代码</p>
headless设置为true可以让浏览器以headless的方式运行,也就是指浏览器不用在界面中打开,它会在后台运行,用户是看不到浏览器的。browser.newPage()将新生成一个标签页。后面的操作基本就围绕着生成的page来进行。
接下来我们让浏览器导航到start url。
<p> ...
<br />
// navigate to url
try {
await page.goto(url, {waitUntil: 'domcontentloaded'});
await page.waitFor(2000);
} catch (e) {
console.error(e);
<br />
// close browser
browser.close();
<br />
// exit code 1 indicating an error happened
code = 1;
process.emit("exit ");
process.reallyExit(code);
<br />
return
}
<br />
...
复制代码</p>
这里trycatch的操作是为了处理浏览器访问超时的错误。当访问超时时,设置exit code为1表示该任务失败了,这样Crawlab会将该任务状态设置为FAILURE。