文章目录
前言
用urlopen+Beautifulsoup完成一个学校新闻标题的爬取任务,具体要求如下:
(1)爬取地址 https://www.hist.edu.cn/index/sy/kyyw.htm。
(2)爬取新闻列表汇总的地址链接和题目信息。
(3)将爬取的信息存储到文件“xinwen.txt”中,一行显示一条信息,超链接和文本之间用逗号隔开。
- 准备工作:
- 爬取地址:https://www.hist.edu.cn/index/sy/kyyw.htm
- 爬取数据:新闻的标题
- 环境需求:安装扩展库 BeautifulSoup、urllib
一、扩展库的简要介绍
1、urllib 库
urllib 库是 python 内置的 HTTP 请求库,包括以下模块:
- urllib.request 请求模块
- urllib.error 异常处理模块
- urllib.parse url解析模块
- urllib.robotparser robots.txt解析模块
(1)urllib.request.urlopen()
urlopen 返回的 response 对象是 http.client.HTTPResponse 类型,主要包含 read()、readinfo()、getheader(name)、getheaders()、fileno() 等方法,我们需要用到的是 read() 方法,如下:
# 导入 urllib.request 模块
import urllib.request
# 解析我们给定的 url: https://www.hist.edu.cn/index/sy/kyyw.htm
# 即读取给定 url 的 html 代码 --> 输入 url, 输出 html
response = urllib.request.urlopen('https://www.hist.edu.cn/index/sy/kyyw.htm')
# 打印我们获取到的数据,并以 utf-8 形式进行解码
# 需要注意:修改 pycharm 的编码方式为 utf8, 否则会报错
print(response.read().decode('utf-8'))
2、BeautifulSoup 库
BeautifulSoup 是 Python 的一个第三方库,可以用来解析 html 数据,我们使用它的主要功能就是从网页抓取数据:
- BeautifulSoup 提供一些简单的、python 式的函数用来处理导航、搜索、修改分析树等功能
- BeautifulSoup 通过解析文档为用户提供需要抓取的数据
- Beautiful Soup 自动将输入文档转换为 Unicode 编码,输出文档转换为 utf-8 编码
(1)BeautifulSoup()
BeautifulSoup 将复杂的 HTML 文档转换成一个复杂的树形结构,每个节点都是 Python对象。所有对象可以归纳为4种类型:
- Tag:HTML 或者 XML 中的标签
- NavigableString:标签中的文本内容(不包含标签)
- BeautifulSoup:BeautifulSoup 对象表示一个文档的全部内容,支持遍历文档树和搜索文档树
- Comment:HTML和XML中的注释
我们可以利用 BeautifulSoup() 将上面读取到的 html 文档进行转换,如下:
(2)find_all()
find_all() 为搜索文档树,搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。返回值类型是 bs4.element.ResultSet,即返回的是多个该对象的集合,可以用for循环遍历,完整的语法如下:
find_all( name, attrs, recursive, string, **kwargs)
参数意义如下:
- name:查找所有名字为 name 的 tag。
- attr:tag的属性(即标签的属性)
- string:搜索文档中string字符串的内容
- recursive: 调用 tag 的 find_all() 方法时,BeautifulSoup 会检索当前 tag 的所有子孙节点;如果只想搜索tag的直接子节点,可以使用参数 recursive=False
上面我们已转换 html 文档,接下来我们使用 find_all() 获取我们想要的数据,但在此之前,我们需要知道我们搜索数据的条件是什么?这需要我们通过调试网页得知,具体如下:
- 通过调试网页我们知道,新闻标题所在元素全部包含在lib标签下,外层被 div 标签所包裹, 该 div 标签具有属性 class=“list-main-warp”,因此我们可以使用 find_all() 获取名字为 div 且属性为 class="list-main-warp"的 tag 数据,然后再使用 find_all() 从已获取的div数据里获取名字为 li 的 tag 数据,然后打印输出其中的标题title
代码实现:
# 获取转换后的html文档里属性class=list-main-warp
divs = soup.find_all('div',{'class','list-main-warp'})
# 从已获取的 div 标签的内容里获取 li 标签的内容
lis = divs[0].find_all('li')
# 遍历获取到的 lis 列表,并从中抓取链接和标题
for li in lis:
# print(li.find_all('a')[0].get('href'))
print(li.find_all('a')[0].get('title'))
输出结果:
有了以上基础之后,最后完成第三步,将获取到的信息写入文件。
# 创建文件并写入信息
with open('xinwen.txt','w') as fp:
for li in lis:
url2 = li.find_all('a')[0].get('href')
# parse.urljoin(former,later)将相对的地址组合成一个绝对地址url,对于输入没有限制,开头必须是http://,否则将不组合前面。用former的域名拼接later的路径,如果later有域名,则进行忽略
url2 = parse.urljoin(url,url2)
title = li.find_all('a')[0].get('title')
fp.write(url + ',' + title +'\n')
二、bug及解决方法
在运行时报错You provided Unicode markup but also provided a value for from_encoding
原因是在用BeautifulSoup()将html文档进行转换时规定了编码,python3缺省的编码是unicode, 再在from_encoding设置为utf8, 会被忽视掉,删掉【from_encoding=“utf-8”】就又可以完美运行啦!
三、完整代码
import urllib.request
from bs4 import BeautifulSoup
from urllib import parse
# 解析我们给定的 url: https://www.hist.edu.cn/index/sy/kyyw.htm
# 即读取给定 url 的 html 代码 --> 输入 url, 输出 html
url = "https://www.hist.edu.cn/index/sy/kyyw.htm"
response = urllib.request.urlopen(url)
# 打印我们获取到的数据,并以 utf-8 形式进行解码
# 需要注意:修改 pycharm 的编码方式为 utf8, 否则会报错
content = response.read().decode('utf-8')
# 利用 BeautifulSoup()将上面读取到的html文档进行转换
# soup = BeautifulSoup(content,'html.parser',from_encoding='utf-8')
# ps:python3缺省的编码是unicode, 再在from_encoding设置为utf8, 会被忽视掉,去掉【from_encoding="utf-8"】就不会报错了
soup = BeautifulSoup(content,'html.parser')
# 获取转换后的html文档里属性class=list-main-warp
divs = soup.find_all('div',{'class','list-main-warp'})
# 从已获取的 div 标签的内容里获取 li 标签的内容
lis = divs[0].find_all('li')
# 遍历获取到的 lis 列表,并从中抓取链接和标题
# for li in lis:
# print(li.find_all('a')[0].get('href'))
# print(li.find_all('a')[0].get('title'))
# 创建文件并写入信息
with open('xinwen.txt','w') as fp:
for li in lis:
url2 = li.find_all('a')[0].get('href')
# parse.urljoin(former,later)将相对的地址组合成一个绝对地址url,对于输入没有限制,开头必须是http://,否则将不组合前面。用former的域名拼接later的路径,如果later有域名,则进行忽略
url2 = parse.urljoin(url,url2)
title = li.find_all('a')[0].get('title')
fp.write(url + ',' + title +'\n')
版权声明:本文参考了CSDN博主「安悦悦ya」的原创文章。
原文链接:https://blog.csdn.net/pola_/article/details/121316947