前言
从上一篇推文中介绍了简单的网络爬虫代码框架和检查页面元素的三个步骤,这也是使用网络爬虫的基本操作。
import requests
def getHTML(url):
r = requests.get(url)
r.encoding = r.apparent_encoding
return r.text # r.text->文本形式返回 r.content->二进制形式
在上一篇推文中也简单介绍了为什么没使用try-except捕获异常,在爬虫代码编写时让异常抛出来更便于我们发现问题解决问题。在本文中将介绍网络爬虫获取数据后如何进行解析,提取出我们需要的内容,以及对提取结果的存储。
数据解析——BeautifulSoup
实际上网络爬虫代码并不会完全按照“前言”部分列出的示例代码那样简单,如果抓取成功,我们可以获取到网页的前端源代码,也就是HTML (Hyper Text Markup Language,超文本标记语言),但是我们需要的信息可能只是其中的图片或者某一部分特定的文本。
HTML中包含的内容太多,要从中找出需要的信息工作量比较大。在Python中有丰富的第三方库和标准库,我们可以使用这些第三方库和标准库进行解析数据,而HTML中是包含有标签对的,标签对内部也会设置一些id class等元素,如果能够利用这些标签对信息,那么解析数据就要简单很多了。在这里主要介绍一个BeautifulSoup库。
Windows键+R组合键,输入cmd回车打开命令行,输入如下的BeautifulSoup的安装指令,注意:末尾有一个数字4,安装时是安装的beautifulsoup4.
pip install beautifulsoup4
使用BeautifulSoup时,我们需要导入这个安装的第三方库,导入方法如下。这时候写的是 bs4 与 BeautifulSoup 。
from bs4 import BeautifulSoup
到此,BeautifulSoup 的安装与导入就已经介绍完了,接下来介绍其中的常用方法。假定BeautifulSoup对象名为soup,需要提取 class 属性为 test 的 a 标签。
方法 | 作用 | 示例 |
---|---|---|
find(标签, 属性) | 提取首个满足要求的标签元素 | soup.find('a', class_='test') |
find_all(标签, 属性) | 提取所有满足要求的标签元素 | soup.find_all('a', class_='test') |
注意提取的是 class 属性为 test 的 a 标签,但示例中写的是 class_='test' 多了一个下划线,与 Python 的关键字 class 进行区分。
对于提取到的具体标签元素,假定变量名为 tag,我们可以使用的方法如下表所示。
属性/方法 | 作用 |
---|---|
find() 、 find_all() | 可以继续查找标签内包含的下一级标签元素 |
tag.text | 提取标签内的文字 |
tag['属性名'] | 提取标签内对应属性值 |
02
爬取百度阅读书籍名称
有了上述的了解之后,直接进行一个简单的爬虫实战,在应用中学习。
此次抓取的内容是“百度阅读”中的“分类 --> 计算机 --> IT人文”中前10页的所有书籍名称、作者与价格,按以下顺序就可以进入到待抓取页面。或者直接在浏览器中打开这个链接跳转到目标页面:https://yuedu.baidu.com/book/list/3005?show=0
要获取前10页的内容,那么我们需要得到这10页的链接,滑到页面底部,点击第2页、第3页,查看导航栏处的链接变化情况,对比一下这几页的链接有什么不同。
页码 | 链接 |
---|---|
第1页 | https://yuedu.baidu.com/book/list/3005?show=0 |
第2页 | https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=20 |
第3页 | https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=40 |
从后两个链接看的出来,问号后面页三个参数赋值表达式,而且只有pn参数有所不同,这时候我们再点回到第1页,会发现第1页的链接变成了:https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=0 ,也就是说第1页其实默认隐藏了 od 和 pn 两个参数。现在重新对比一下就能明显发现只有 pn 不同,而且相邻两页的 pn 相差 20。
页码 | 链接 |
---|---|
第1页 | https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=0 |
第2页 | https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=20 |
第3页 | https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn=40 |
恰好一个页面就是20本书籍的信息,因此我们可以得到书籍页面链接的计算公式。当然,下面的代码在Python中可以写成一行,下面这种写法是为了更便于阅读。
url_list = []
for i in range(10): # 从 0 开始,前 10 页
pn = 20 * i # 每页 20 条数据
url_list.append("https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn={}".format(pn))
解决了链接构造问题,现在就针对其中具体的某一个链接进行分析,获取需要的数据。步骤与上一篇推文介绍的一致。
定位到这一版的 div 标签,就已经距离我们需要的数据不远了,也可能第3步操作的时候,点击完之后,展示的不是列表状态的 div 标签,是展开状态的单个 div ,只需要点击它前面的三角形符号把它收起来就可以了。现在我们已经可以完成一部分代码了。
import requests
from bs4 import BeautifulSoup
def getHTML(url):
'''传入 url ,返回抓取到的 html 页面内容'''
r = requests.get(url)
r.encoding = r.apparent_encoding # 防乱码
return r.text # 返回 HTML 页面内容
def parse(html):
'''把 html 传进来进行解析'''
soup = BeautifulSoup(html, 'html.parser') # 解析页面
# 获取所有 class=book 的 div 标签
books = soup.find_all('div', class_='book')
# TODO:还需要进一步提取其中的书籍名称、作者、价格
def main():
# 构建链接
url_list = ['https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn={}'.format(i*20) for i in range(10)]
# 依次获取 10 个页面内容
for url in url_list:
html = getHTML(url) # 请求页面,获取 HTML
parse(html) # 解析 HTML
if __name__ == '__main__':
main()
上述代码中只有打了 TODO 标记的位置没有完成,我们获取到了 div 标签还不行,还要获取更加详细的书籍信息。点击网页中 div 元素前面的三角形,展开 div 元素,里面是两个 a 标签一个 p 标签,再继续展开,直到当前这个 div 元素全部展开为止。最终展开的效果如下图所示。
通过观察发现,它们都位于 span 标签内,而且 class 属性不同,因此可以对 div 标签使用 find() 方法进一步获取这三个 span 标签。需要注意的一点是,这里价格所在的 span 标签 class 属性有两个,一个 price 一个 price-free ,结合文字“免费”可以想到 price-free 是免费书本独有的,我们需要获取前 10 页所有书籍信息,所以在使用 find 方法填写属性值的时候只要填写 price 就可以了。
现在重新编写 parse 函数
def parse(html):
'''把 html 传进来进行解析'''
soup = BeautifulSoup(html, 'html.parser') # 解析页面
# 获取所有 class=book 的 div 标签
books = soup.find_all('div', class_='book')
# 提取当前页面每一本书的信息
for book in books:
title = book.find('span', class_='title').text.strip() # 标题
author = book.find('span', class_='author').text.strip() # 作者
price = book.find('span', class_='price').text.strip() # 价格
print(title, author, price) # 结果输出
在使用 find() 函数之后,使用 .text 获取标签中包含的文本,最后使用 Python 中处理字符串的 strip() 方法去除提取到的文本中开头和结尾的空白 (空格和换行)。
上图是爬取结果部分截图展示,截图中倒数第 3 行输出只有 ¥ 符号,并不是爬取失败,经过查看页面内容,发现该处展示的书籍信息仅有一个 ¥ 符号而没有其他任何信息。
03
存储数据——CSV
上面两节内容介绍了使用 BeautifulSoup 解析 HTML 数据,并通过爬取百度文库书籍信息这个实验对 BeautifulSoup 库的实际应用进行了代码演示。网络爬虫其实无非就是获取数据、解析数据、存储数据,这三个步骤每一个步骤都有很多解决方案,可以都是简单了解,但也要熟练使用其中一种方法。
现在介绍最后一步:存储数据。这里我们使用 CSV 格式进行存储,之所以选用这个格式存储是因为这个格式构造简单,而且最终存储的 CSV 文件在 windows 上双击会使用 excel 软件打开,以表格形式呈现。
CSV 文件格式要求:同一行不同列的数据之间使用英文逗号进行分隔,不同行之间使用换行符进行分隔。
从格式要求中我们能够了解到,数据中间一定不能出现英文逗号和换行符,一定要把这些内容替换掉。Python 中有一个第三方库 csv 可以帮助我们将数据写入到 CSV 文件,但需要安装和记忆一些方法,完全没有必要,我们直接按照 CSV 的格式要求,使用 Python 的 open 方法打开文件写入即可。
以下三个函数有代码修改,修改后如下所示,其余部分代码没有改动。
def parse(html):
'''把 html 传进来进行解析'''
soup = BeautifulSoup(html, 'html.parser') # 解析页面
# 获取所有 class=book 的 div 标签
books = soup.find_all('div', class_='book')
# 提取当前页面每一本书的信息
infos = []
for book in books:
title = book.find('span', class_='title').text.strip() # 标题
author = book.find('span', class_='author').text.strip() # 作者
price = book.find('span', class_='price').text.strip() # 价格
infos.append((title, author, price)) # 元组形式存入列表中
return infos # 结果返回
def save(infos):
'''存储数据'''
# 追加方式写入
with open("infos.csv", 'a', encoding='utf-8') as f:
for info in infos:
line = ','.join(info) # 1.同一行不同列使用英文逗号分隔
# 英文逗号和换行符换成空格,以免格式错乱
line = line.replace(',', ' ').replace('\n', ' ')
line = line + '\n' # 2.不同列之间使用换行符分隔
f.write(line)
def main():
# 构建链接
url_list = ['https://yuedu.baidu.com/book/list/3005?od=0&show=0&pn={}'.format(i*20) for i in range(10)]
# 依次获取 10 个页面内容
for url in url_list:
html = getHTML(url) # 请求页面,获取 HTML
infos = parse(html) # 解析 HTML
save(infos) # 存储信息
至此,我们已经完成了网络爬虫的最后一个步骤:存储数据。存储数据方法有很多,写 CSV 文件以表格形式展示,是我个人认为比较简单方便的一种方式,只是要注意写入的数据中不能出现英文逗号与换行符,否则最终写出的内容会格式错乱。
存储数据方法也可以选择写入到 txt 或者 json ,又或者自己定义格式,也可以存储数据库,但不论使用何种方法,目的都是一致的,那就是将爬虫获取的目标信息保留下来,这些方法能够熟练用一种,其他的能够从网络上查找相关代码就已经可以了。
04
总结
对数据解析我们使用了 BeautifulSoup ,存储数据使用了 CSV 格式,但不论使用何种方法,解决的都是 数据解析与数据存储 这两个问题。至此,已经完成了一个完整实现了 数据获取、数据解析与数据存储 三步骤的爬虫程序,抓取不同网页我们会使用不同的方法,但从步骤上都是相同的,按照这样的思路也就能很快的完成爬虫程序的编写。
在最后Last but not least
编写爬虫程序的入门门槛低,只要掌握基础语法,再稍加了解爬虫知识就能上手练习。到目前为止我们编写的程序就已经能够在不少场景中进行数据抓取了。但是目前的程序也有一定的问题,例如抓取频率没有控制,可能被反爬,在需要大量抓取数据时又显得速度太慢等等,这都是之后需要逐渐解决的问题。
关于Python技术储备
学好 Python 不论是就业还是做副业赚钱都不错,但要学会 Python 还是要有一个学习规划。最后大家分享一份全套的 Python 学习资料,给那些想学习 Python 的小伙伴们一点帮助!
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
三、Python视频合集
观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
四、实战案例
光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
五、Python练习题
检查学习结果。
六、面试资料
我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
最后祝大家天天进步!!
上面这份完整版的Python全套学习资料已经上传至CSDN官方,朋友如果需要可以直接微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】。