赞
踩
目录
把爬虫的过程模块化,基本上可以归纳为以下几个步骤:
[√] 分析网页URL:打开你想要爬取数据的网站,然后寻找真实的页面数据URL地址;
[√] 请求网页数据:模拟请求网页数据,这里我们介绍requests
库的使用;
[√] 解析网页数据:根据请求获得的网页数据我们用不同的方式解析成我们需要用的数据(如果网页数据为html源码,我们用Beautiful Soup
、xpath
和re正则表达式
三种解析;若网页数据为json格式,我们可以直接用字典列表等基础知识处理)
[√] 存储网页数据:一般来说,解析后的数据是比较结构化的,可以保存为txt、csv、json或excel等文本,亦或者可以存储在数据库如MySql、MongoDB或SqlLite中。
当我们有一个目标网站,有时候会发现对于静态网页
,我们只需要把网页地址栏中的URL传到get请求中就可以直接取到网页的数据。但如果这是动态网页
,我们便无法通过简单的传递网页地址栏的URL给get请求来获取网页数据,往往这个时候,我们进行翻页的时候还会发现网页地址栏中的URL是不会发生变化的。
接下来,我们来分别介绍这两种情况下如何获取真实的页面数据URL地址。
对于静态网页来说,其实网页地址栏中的URL就是我们需要的。
以 贝壳二手房网
(https://bj.ke.com/ershoufang/) 为例,我们可以看到进行翻页(如到第2页)的时候网页地址栏的URL变为了(https://bj.ke.com/ershoufang/pg2/)。类型这种情况,多半就是静态网页了,而且翻页的URL规律十分明显。
对于动态网页来说,我们一般可以通过以下几个步骤找到真实URL地址:
需要按“F12”进入到浏览器的开发者模式;
点击“Network”—>XHR或JS或者你全部查找看;
进行翻页(可能是点击下一页或者下滑加载更多);
观察第2步中name模块的内容变化,寻找。
以 虎牙星秀区
(https://www.huya.com/g/xingxiu) 为例,我们可以看到进行翻页(如到第2页)的时候网页地址栏的URL没有发生任何改变。
为了便于找到真实的URL地址,我们可以在开发者模式中找以下截图中的几点,preview
是预览结果,可以便于我们进行匹配定位具体的Name
。
当我们定位到具体的Name后,右侧选择Headers
可以查看到请求网页需要的相关参数信息,而且比较好拟清其变化规律。以虎牙星秀为例,其真实URL地址及变化规律如下:
URL= 'https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId=1663&tagAll=0&page=2'
基础 url 地址如下:
url = 'https://www.huya.com/cache.php’
请求参数parames如下:
- parames = {
- 'm': 'LiveList',
- 'do': 'getLiveListByPage',
- 'gameId': 1663,
- 'tagAll': 0,
- 'page': 2, # 翻页变化的就是这个参数
- }
当我们确定了真实数据的URL后,这里便可以用requests
的get或post方法进行请求网页数据。
关于requests库的更多使用方式,大家可以前往(https://requests.readthedocs.io/zh_CN/latest/)查看。
- In [1]: import requests
-
- In [2]: url = 'https://bj.ke.com/ershoufang/'
-
- In [3]: r = requests.get(url)
-
- In [4]: type(r)
- Out[4]: requests.models.Response
-
- In [5]: r.status_code
- Out[5]: 200
我们得到的是一个Response对象,如果我们想要获取网页数据,可以使用text
或content
属性来获取,另外如果获取的网页数据是json格式的则可以使用Requests 中内置的 **json()**解码器方法,助你处理json 数据。
r.text:字符串类型的数据,一般网页数据为文本类用此属性
r.content:二进制类型的数据,一般网页数据为视频或者图片时用此属性
r.json():json数据解码,一般网页数据为json格式时用此方法
对于一些动态网页,请求的网址是基础url和关键字参数组合而成,这个时候我们可以使用 params
关键字参数,以一个字符串字典来提供这些参数。
- In [6]: url = 'https://www.huya.com/cache.php'
- ...: parames = {
- ...: 'm': 'LiveList',
- ...: 'do': 'getLiveListByPage',
- ...: 'gameId': 1663,
- ...: 'tagAll': 0,
- ...: 'page': 2, # 翻页变化的就是这个参数
- ...: }
- ...:
- ...: r = requests.get(url, params=parames)
-
- In [7]: r.url
- Out[7]: 'https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId=1663&tagAll=0&page=2'
通常,你想要发送一些编码为表单形式的数据——非常像一个 HTML 表单。要实现这个,只需简单地传递一个字典给 data 参数。你的数据字典在发出请求时会自动编码为表单形式:
- >>> payload = {'key1': 'value1', 'key2': 'value2'}
-
- >>> r = requests.post("http://httpbin.org/post", data=payload)
很多时候你想要发送的数据并非编码为表单形式的。如果你传递一个 string
而不是一个 dict
,那么数据会被直接发布出去。
- >>> import json
-
- >>> url = 'https://api.github.com/some/endpoint'
- >>> payload = {'some': 'data'}
-
- >>> r = requests.post(url, data=json.dumps(payload))
此处除了可以自行对 dict
进行编码,你还可以使用 json
参数直接传递,然后它就会被自动编码。
- >>> url = 'https://api.github.com/some/endpoint'
- >>> payload = {'some': 'data'}
-
- >>> r = requests.post(url, json=payload)
在模拟请求时,如果不设置请求头的话是比较容易被网站发现是来自爬虫脚本,一些网站会对这种模拟请求进行拒绝。因此我们可以简单设置一下请求头做伪装,一般是设置浏览器。
- headers = {
- "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36",
- }
- r = requests.get(url, headers=headers)
其实,对于请求头还可以设置很多参数,具体大家可以在实际爬虫过程中在开发者模式看看里面的请求头模块进行分析处理。
虎牙星秀请求头
我们在 2.1 中看到获取响应码的是通过 r.status_code
属性,一般来说如果 返回 数字 200,则表示成功获取了网页数据。
响应码分为五种类型,由它们的第一位数字表示:1xx:信息,请求收到,继续处理 2xx:成功,行为被成功地接受、理解和采纳 3xx:重定向,为了完成请求,必须进一步执行的动作 4xx:客户端错误,请求包含语法错误或者请求无法实现 5xx:服务器错误,服务器不能实现一种明显无效的请求
上面有提到我们请求的网页数据有Html源码文本或者是json字符串文本,两者的解析方式不同。以下我们分别进行简单说明,大家在实际操作中视情况而定即可。
对于网页html文本来说,这里介绍Beautiful Soup
、xpath
和re正则表达式
三种解析方法。
以贝壳二手房最新房源(https://bj.ke.com/ershoufang/co32/)为例,其html源码如下,我们通过get请求后的数据进行解析。
贝壳二手房
关于Beautiful Soup库的更多使用方式,大家可以前往查看(https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/)
首先安装pip install beautifulsoup4
。
我们将网页html文本内容r.text
当作第一个参数传给BeautifulSoup
对象,该对象的第二个参数为解析器的类型(这里使用lxml
),此时就完成了BeaufulSoup
对象的初始化。然后,将这个对象赋值给soup
变量。
- from bs4 import BeautifulSoup
- import requests
-
- url = 'https://bj.ke.com/ershoufang/co32/'
- r = requests.get(url)
- soup = BeautifulSoup(r.text, 'lxml')
房源名称
获取房源的名称的代码如下:
- # 获取全部房源 所在的节点
- sellList = soup.find(class_="sellListContent")
- # 获取全部房源节点列表
- lis = sellList.find_all('li',class_="clear")
- # 选取第一个房源节点
- div = lis[0].find('div',class_="info clear")
- # 采集房源名称
- title = div.find('div',class_="title")
- print(title.text)
明春西园 2室1厅 南 北
房源其他信息大家可以自己处理,强化学习!
XPath,全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言。
首先安装lxmlpip install lxml
。
常见的规则如下:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从当前节点选取直接子节点 |
// | 从当前节点选取子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
首先导入 lxml 库的 etree 模块,然后声明一段 HTML 文本,调用 HTML 类进行初始化,成功构造一个 XPath 解析对象。
- from lxml import etree
- import requests
-
- url = 'https://bj.ke.com/ershoufang/co32/'
- r = requests.get(url)
- html = etree.HTML(r.text)
右键可获取xpath
通过copy获取的xpath://*[@id="beike"]/div[1]/div[4]/div[1]/div[4]/ul/li[1]/div/div[1]/a
- # 获取 全部房源所在节点 ul,根据属性匹配精准查找
- ul = html.xpath('.//ul[@class="sellListContent"]')[0]
- # 获取房源列表
- lis = ul.xpath('.//li[@class="clear"]')
- # 选取第一个房源节点
- li = lis[0]
- # 获取其房源名称
- li.xpath('./div/div[1]/a/text()')
['明春西园 2室1厅 南 北']
其他房源信息,大家可以自行处理,强化学习!
- # 找到房源名称所在的前后字符,然后组成正则表达式
- re.findall(r'<a class="VIEWDATA CLICKDATA maidian-detail" title="(.*?)"',r.text,re.S)[0]
'明春西园 2室1厅 南 北'
常见的正则表达式匹配模式如下表
模式 | 描述 |
---|---|
^ | 匹配字符串的开头 |
$ | 匹配字符串的末尾。 |
. | 匹配任意字符,除了换行符,当.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。 |
[...] | 用来表示一组字符,单独列出:[amk] 匹配 'a','m'或'k' |
[^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。 |
* | 匹配0个或多个的表达式。 |
+ | 匹配1个或多个的表达式。 |
? | 匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式 |
{ n} | 精确匹配 n 个前面表达式。例如, o{2} 不能匹配 "Bob" 中的 "o",但是能匹配 "food" 中的两个 o。 |
{ n,} | 匹配 n 个前面表达式。例如, o{2,} 不能匹配"Bob"中的"o",但能匹配 "foooood"中的所有 o。"o{1,}" 等价于 "o+"。"o{0,}" 则等价于 "o*"。 |
{ n, m} | 匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式 |
a|b | 匹配a或b |
() | 对正则表达式分组并记住匹配的文本 |
(?imx) | 正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。 |
(?-imx) | 正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。 |
(?: ) | 类似 (...), 但是不表示一个组 |
(?imx: ) | 在括号中使用i, m, 或 x 可选标志 |
(?-imx: ) | 在括号中不使用i, m, 或 x 可选标志 |
(?#...) | 注释. |
(?= ) | 前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。 |
(?! ) | 前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功 |
(?> ) | 匹配的独立模式,省去回溯。 |
\w | 匹配字母数字及下划线 |
\W | 匹配非字母数字及下划线 |
\s | 匹配任意空白字符,等价于 [ \t\n\r\f]。 |
\S | 匹配任意非空字符 |
\d | 匹配任意数字,等价于 [0-9]. |
\D | 匹配任意非数字 |
\A | 匹配字符串开始 |
\Z | 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。 |
\z | 匹配字符串结束 |
\G | 匹配最后匹配完成的位置。 |
\b | 匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
\B | 匹配非单词边界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
\n, \t, | 匹配一个换行符,匹配一个制表符 |
\1...\9 | 匹配第n个分组的内容。 |
\10 | 匹配第n个分组的内容,如果它经匹配。否则指的是八进制字符码的表达式。 |
举例:
[Pp]ython | 匹配 "Python" 或 "python" |
rub[ye] | 匹配 "ruby" 或 "rube" |
[aeiou] | 匹配中括号内的任意一个字母 |
[0-9] | 匹配任何数字。类似于 [0123456789] |
[a-z] | 匹配任何小写字母 |
[A-Z] | 匹配任何大写字母 |
[a-zA-Z0-9] | 匹配任何字母及数字 |
[^aeiou] | 除了aeiou字母以外的所有字符 |
[^0-9] | 匹配除了数字外的字符 |
在requests提供了r.json(),可以用于json数据解码,一般网页数据为json格式时用此方法。除此之外,还可以通过json.loads()和eval()方法进行处理。
json文本解析
- url = 'https://www.huya.com/cache.php'
- parames = {
- 'm': 'LiveList',
- 'do': 'getLiveListByPage',
- 'gameId': 1663,
- 'tagAll': 0,
- 'page': 2, # 翻页变化的就是这个参数
- }
-
- r = requests.get(url, params=parames)
- data = r.json()
- type(data)
dict
如此解析后得到的数据就是字典,然后我们在看看字典中哪些字段是我们需要的,取出即可。
当我们获取了到想要的数据后,便可以写入本地了。
对于文本类数据,可以通过csv模块或pandas模块进行写入到本地csv文件或excel文件;同时也可以用pymysql模块写入到数据库或者sqlite写入到本地数据库。
对于视频或者图片,可以open一个文件然后写入二进制内容后保存本地亦可。
关于存储数据大家可以结合实际案例进行学习。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。