0x01 写在前面
本文不会讲到如何使用 Scrapy 框架,也不会讲述 requests 自己构建爬虫。
而是重在一种过程:如何快速利用所有手段构建一个自己想爬取信息的小爬虫。
本文只在乎了流程,其中还有很多小细节需要细细完善。一个好的爬虫,需要花费大量的精力。
本文仅以学习技术的目的进行技术的学习,乱爬别人网站是不好的,严重的情况下可能影响别人正常的业务。请严格遵守所在地的法律法规,完成技术学习后立刻停止对别人的爬取。
0x02 工具介绍&使用介绍
Scrapy
Scrapy(/ˈskreɪpi/ SKRAY-pee是一个用Python编写的自由且开源的网络爬虫框架。它在设计上的初衷是用于爬取网络数据,但也可用作使用API来提取数据,或作为生成目的的网络爬虫。该框架目前由网络抓取的开发与服务公司Scrapinghub公司维护。
Scrapy项目围绕“蜘蛛”(spiders)建构,蜘蛛是提供一套指令的自包含的爬网程序(crawlers)。遵循其他如Django框架的一次且仅一次精神,允许开发者重用代码将便于构建和拓展大型的爬网项目。Scrapy也提供一个爬网shell,开发者可用它测试对网站的效果。
——摘自维基百科。
简单说就是一个专门为开发爬虫设计的,比较适合大爬虫,这时候的性能优化、调度啥的都很方便,但是小爬虫就反而显得麻烦了。因此本文不会重点使用他,仅仅是简单利用一下他。
requests
Requests is a Python HTTP library, released under the Apache License 2.0. The goal of the project is to make HTTP requests simpler and more human-friendly. The current version is 2.25.0
Requests is one of the most popular python libraries that is not included with python, it has been proposed that requests be distributed with python by default
——摘自维基百科。
知识储备
看懂本文至少需要会哪些知识
Html+CSS+js
由于爬虫就是和网页打交道,因此前端的各种语言不说精通,但是基本的使用还是应知道的。
python
这个就有点废话了,本文就是基于 python
来进行爬取的。
requests
不需要知道全部,这里只需要知道这个是 python
的一个库,可以用来进行 http
的请求的即可。需要的时候再到官方文档查询需要的函数。
浏览器开发者工具的基本使用
为了选中各种元素,需要通过浏览器的开发者模式对前端进行调控和选择,因此基本的开发者调试功能的必须的。
Scrapy
不需要知道那么多,就使用下面的这种也就够了。
Scrapy shell 使用
打开一个 终端,运行命令。
格式如下:
1 | scrapy shell url |
例如,输入下面的命令并运行,
1 | scrapy shell https://app.mi.com/details?id=com.planet.light2345 |
使用 Scrapy
的 shell
会返回一个 ipython
的会话,其中的封装好了一个可以直接使用的对象 response
。
内置的 response
对象,封装了很多方法,这里主要使用其中的 xpath
方法。更多请查阅官方文档。
0x03 环境安装
Scrapy 安装
1 | pip install scrapy |
requests 安装
1 | pip install requests |
0x04 实战演示
目标
目标站点
笔者通过百度搜索,找到了目标网站—— “小米应用商店”。
这里感谢小米,感谢雷军。
爬取目的
本次爬取是希望将 小米应用市场
中的所有的有效的 热门应用
的隐私政策爬取出来,并进行简单的数据清洗。
step-by-step
准备工作
1、浏览器访问此网站,并打开开发者模式。(F12)。
2、打开一个此网站的 Scrapy shell
。
初步分析
首先是如何获取每个页面。将页面滚动到底部,可以发现有多个页面。
随便点击一个页面,观察浏览器顶部 url
中的变化,可以发现页面获取的 get
参数信息。
1 | # 点击页面 2 时 |
所以任意页面的参数就是 page
参数。所以,我们只需要将最后的参数进行枚举即可以遍历每个页面。
至于页面的总数量,偷懒可以直接硬编码 23,因为我们都肉眼可见就是 23 页。想让程序蒲适性好些就可以通过页面标签来获取。
于是就可以通过 Scrapy 提供的 shell 工具获取我们所希望的信息了。
定位总页码
step1:
首先通过浏览器的开发者工具选中我们要定位的目标。
step2:
然后右键该元素,并选择 复制
-> 复制 xpath
。从而得到目标元素的 xpath
路径,熟悉的读者也可以自己手动定位。
step3:
然后切换到前面打开的 Scrapy
的 shell
终端中,输入下面的代码,并执行。
1 | response.xpath("浏览器中复制的xpath") |
如下图所示,返回值是一个列表,其中的第一个元素就是我们选择的元素。
step4:
然后通过 text()
获取此标签中的文本值。输入以下代码,获取值。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[2]/a[7]/text()") |
可以看到返回的值的确包含了想要的数据 23,但是仍然是一个对象的列表。
所以可以通过内置的 get
函数获取其中的 data
的内容(字符串格式)
1 | response.xpath("/html/body/div[6]/div/div[1]/div[2]/a[7]/text()")[0].get() |
于是得到了页面的数量,记得通过 int
进行类型转化。
为了发包方便,笔者这里封装了一个函数 send
来实现。
1 | def send(url): |
所以可以得到下面的代码。
1 | def getPageNum(url): |
获取页面中 app 的 url
有了上述的页面,就可以用同样的方法获取每个页面中每个 app
的 url
。
step1:
使用开发者工具选中所有 app
的列表。
如下图所示,此 ul
包含了本页面中的所有的应用。然后就可以选择其中的所有元素。
step2:
同样右键复制其 xpath
路径。再在前面打开的 Scrapy
的 shell
终端中,输入下面的代码,并执行。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[1]/ul") |
可以看见获取是成功的。
step3:
而通过观察,其中的每个 app
都对应者一个 li
。因此前面的路径下面,可以添加一个 li
即可以选中所有的应用的 li
。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[1]/ul/li") |
可以看到,返回的值是一个 python
中的列表,列表的每个元素都是其中的一个 li
对应的选择器。
但是事实上,这里只需要其中的每个 li
下面的 a
标签中的链接。于是应该再添加一个 a
。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[1]/ul/li/a") |
和上面类似,只是内容更进一步了,但是还不是我们想要的。
step4:
在上述的基础上,通过观察发现需要获取 a
标签中的 href
的值。
于是再使用下面的方法获取其中的值。使用 @href
可以获取所有的 href
。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[1]/ul/li/a/@href") |
再通过函数 getall()
可以获得所有的这些链接组成的列表。
1 | response.xpath("/html/body/div[6]/div/div[1]/div[1]/ul/li/a/@href").getall() |
于是我们就得到了我们想要的 url
的列表。
1 | def getIdInPagei(pagei): |
获取每个软件隐私政策 url
有了上述的工作之后,便可以通过进入某个 app
对应的页面,从而获取其名字和隐私政策的 url
。
通过观察,可以发现每个应用进入之后都非常一致,在统一的位置都放上的 隐私政策
的链接,这很利于批量爬取。
但是部分应用开发者尚未完善隐私政策的链接,因此后面有些爬取出来会是 “javascript:void(0)” 等各自奇怪的情况。
step1:
重新打开一个新的 Scrapy
的 shell
。这次的 url
应该以某一个具体的 app
为例子。这里我们以某一个作为例子。
1 | scrapy shell https://app.mi.com/details?id=com.planet.light2345 |
step2:
还是同理,通过前面的方法,获取对应的 xpath
路径。
然后这里就直接一步到位,不再啰嗦,就直接自己添加一个 /@href
。
1 | response.xpath("/html/body/div[6]/div[1]/div[5]/div[2]/div[2]/a/@href").get() |
于是这个就是对应的隐私政策的链接了。
step3:
然后需要获取此应用的名字,还是同理选中这个元素。
然后复制其对应的 xpath
并取其中的值即可(添加 /text()
)。
1 | response.xpath("/html/body/div[6]/div[1]/div[2]/div/div/h3/text()").get() |
可以看到,成功正确获取到了此应用的名字。
step4:
结合前面获取的每个 app
的页面,都可以获取其相应的隐私政策的链接和其名字。
1 | def getPriUrlAndName(appId): |
从页面获取隐私政策内容
有了上述的铺垫,即可以将隐私政策从页面中获取下来了。
本次由于是获取文本信息,只需要将整个页面中所有的文本信息全部保存即可完成本次的数据的清洗。
这个需求只需要如下的方法,在 xpath
中已经集成。如下即是获取 body
这个变量名所代表的的 Selector
中的所有文本信息,包括子标签。
1 | body.xpath('string(.)') |
综上,于是可以写成下面的代码
1 | def getPriFromUrl(url, name): |
值得一提的是,直接使用会将页面中的 JS
代码也一并获得,因此需要先将其移除,使用的是如下方法: body.xpath("//script").remove()
。
而使用 try ... except...
则是前面所说的,有些应用开发不完善,其隐私政策的 url
链接是无效的。
跑一个
直接运行。可以看到如下的提示。
然后可以看到文件夹下面生成的爬取文件了。效果还是。
0x05 分析总结
正如开头所说的,本文只是做了一个简单的实验,希望抛砖引玉。其中还有很多细节没有进行完善:如假 header
用于伪装、数据的清洗、尚存的乱码和需要 JS
渲染的页面需要使用 selenium
(有空再将整理后的文档上传)等。本次只是梳理记录一个流程和方法。
更多的 xpath
用法可以参考 parsel (也支持 CSS 选择器)或者 xpath
的官方文档。
更多的 Scrapy
使用方法也请查阅官方文档。
0x06 参考
xpath的一些用法: https://www.cnblogs.com/g2thend/p/12452186.html
parsel文档:https://parsel.readthedocs.io/en/latest/usage.html
提取所有文本: https://blog.csdn.net/MrLevo520/article/details/53158050