Python 数据采集-爬取厦门大学官网新闻标题与链接
前言
列表有一个朋友的学校是厦门大学,想起来之前高三我和室友的约定,我想去集美大学,朋友想去厦门大学,结果,我们两个都没有考上哈哈哈
之前我说要更博文,但是突如其来的一个项目打乱了我的计划,直至今天,我应该是拨乱反正了,所以今天及之后开始继续更博文,之前新闻爬取的小结里说可以以我的思路去尝试一下爬取其他学校的新闻,不知道你们试了没,反正我没试哈哈哈,今天数据采集课无聊,突然想起来,要不带你们爬一下厦门大学官网的新闻吧,验证一下思路是否通用可行
别怀疑了宝,我试过了,思路绝对可行哈哈哈!
⭐ 本文基于学校的课程内容进行再次扩展尝试,所爬取的数据均为学习使用,请勿用于其他用途
- 准备工作:
- 爬取地址:https://alumni.xmu.edu.cn/xwzx/tzgg.htm
- 爬取目的:爬取新闻并对新闻内容进行词频统计分析(词频下篇再更)
- 了解前导文章
- 环境需求:安装扩展库 BeautifulSoup、urllib(⭐不会安装点这里 Python 下载安装第三方库)
- 基本知识:
- 了解网页的基本知识
- 掌握 python 基础语法
- 掌握 python 文件写入的语法
一、爬取栏目的单页内容
00 思路分析
按照之前的思路,所有的新闻整体是被包在一个div标签里,因此我们选择网页调试,找到这个div标签,并找出识别该div标签的条件,之后观察每条新闻是如何展示的,进而找出获取新闻标题及链接的条件,如下图所示:
调试观察可知:
① 所有的新闻整体都被包在一个class属性为tab-content的div标签里
② 每一条新闻的都被包在一个class属性为media-heading的h4标签里,其中新闻链接与新闻标题被包裹在h4标签下的a标签里
③ 至此,我们获取单页内容的思路就基本清晰了:
1> 获取页面整体内容
2> 从页面整体内容内查找并获取class="tab-content"的div标签下的内容
3> 从获取class="tab-content"的div标签下的内容内查找并获取所有class="media-heading"的h4标签下的内容
01 代码实现
接下来动手将思路换成代码,我们在爬取的过程输出打印新闻链接和新闻标题,并将爬取的结果存入urlList.txt 文件中,代码实现如下:
# 厦门大学新闻爬取
# coding=utf-8
import urllib.request
from urllib.parse import urljoin
from bs4 import BeautifulSoup
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen('https://alumni.xmu.edu.cn/xwzx/tzgg.htm')
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
divs = soup.find_all('div', {'class': "tab-content"})
# media-heading
hs = divs[0].find_all('h4')
with open('urlList.txt', 'w', encoding='utf8') as fp:
for h in hs:
url1 = "https://zs.xmu.edu.cn/"
url2 = h.find_all('a')[0].get("href")
# 使用urllib的urljoin()拼接两个地址
# urljoin的第一个参数是基础母站的url, 第二个是需要拼接成绝对路径的url
# 利用urljoin,我们可以将爬取的url的相对路径拼接成绝对路径
url = urljoin(url1, url2)
title = h.findAll('a')[0].text
# 用以提示在爬取中
print(url+','+title)
fp.write(url + "," + title + '\n')
# 用以提示爬取结束
print("爬取结束!!!")
二、实现自动翻页爬取
00 思路分析
浏览器实现翻页是因为我们点击了跳转下一页的链接,然后浏览器根据链接获取下一页的内容展示给我们,同理,实现翻页就是获取下一页的链接,然后我们根据这个链接按照上面爬取单页的思路去爬取每一页,因此我们第一步要做的就是获取下一页的链接,搞清楚了翻页,接下来实现自动翻页,也就是一句循环解决的问题,但关键在于循环结束的条件是什么,即我们怎么判断现在已经到最后一页了?所以第二步要做的就是找出判断当前页是最后一页的条件。所以,宝,现在我们开始网页调试叭:
调试观察可知:
① 所有的按钮都包裹在一个class属性为p_pages的span标签里
② 下一页的链接包裹在一个class属性为p_next p_fun的span标签里
③ 调试第一页的网页,我们可以获取到尾页的链接,即最后一页的链接,因此,要想知道当前页是否为最后一页,只需将当前页的链接与尾页的链接进行比较,如果相等,则是最后一页
至此,我们实现自动翻页的思路就基本清晰了:
1> 从获取的页面整体内容内查找并获取class="p_pages"的span标签下的内容
2> 从获取的class="p_pages"的span标签下的内容内查找并获取class="p_next p_fun"的span标签下的a标签里的相对路径的下一页链接
3> 使用while循环执行以上步骤,实现自动翻页,其中 while 循环结束的条件是当前页的链接等于尾页的链接
01 代码实现
相较于上一步,我们多了while循环实现自动翻页爬取的代码,代码实现如下:
# 厦门大学新闻爬取
# coding=utf-8
import urllib.request
from urllib.parse import urljoin
from bs4 import BeautifulSoup
conUrl = 'https://alumni.xmu.edu.cn/xwzx/tzgg.htm'
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen(conUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
spans = soup.find_all('span', {'class': "p_pages"})
# 获取下一页链接
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
nextPageHref = nextPage[0].find_all('a')[0].get("href")
# 获取尾页(最后一页)链接
lastPage = spans[0].find_all('span', {'class': "p_last p_fun"})
lastPageHref = lastPage[0].find_all('a')[0].get("href")
while conUrl != lastPageHref:
divs = soup.find_all('div', {'class': "tab-content"})
# media-heading
hs = divs[0].find_all('h4')
pageNum = divs[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
with open('urlList.txt', 'a+', encoding='utf8') as fp:
for h in hs:
url1 = "https://zs.xmu.edu.cn/"
url2 = h.find_all('a')[0].get("href")
# 使用urllib的urljoin()拼接两个地址
# urljoin的第一个参数是基础母站的url, 第二个是需要拼接成绝对路径的url
# 利用urljoin,我们可以将爬取的url的相对路径拼接成绝对路径
url = urljoin(url1, url2)
title = h.findAll('a')[0].text
# 用以提示在爬取中
print(url + ',' + title)
fp.write(url + "," + title + '\n')
# 用以提示爬取第几页结束
print("第" + pageNum + "页爬取结束!!!")
spans = soup.find_all('span', {'class': "p_pages"})
pageNum = spans[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
# pageNum 用以识别当前页是否为最后一页
if pageNum != '3':
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
conUrl = nextPage[0].find_all('a')[0].get("href")
# 不同页爬取的下一页链接的缺失的母站url不同
# 用以识别当前页是否为第1页,从而决定需要拼接的基础母站url
if pageNum == '1':
url = "https://alumni.xmu.edu.cn/xwzx/"
# i = i+1
else:
url = "https://alumni.xmu.edu.cn/xwzx/tzgg/"
conUrl = urljoin(url, conUrl)
response = urllib.request.urlopen(conUrl)
content = response.read().decode('utf-8')
# https://alumni.xmu.edu.cn/xwzx/tzgg/2.htm
# 解析
soup = BeautifulSoup(content, 'html.parser')
else:
break
三、实现自动切换不同栏目爬取
00 思路分析
宝,是不是可以看到厦门大学的新闻分很多个栏目,之前爬河科的新闻时也有分不同栏目,但我们只爬了第一个栏目,这次我们加点难度,把所有栏目下的新闻都爬下来。这就要求我们做到在爬完一个栏目后自动切换到其他栏目继续爬取,直到所有栏目都爬取完。
实现自动切换不同栏目的本质上和实现自动翻页爬取极其相似,浏览器实现切换栏目就是因为我们点击栏目对应的链接,浏览器根据链接跳转到对应的栏目,同理,实现自动切换不同栏目就是我们从网页中获取栏目的链接然后给浏览器,宝,想一想,这和上一步是不是极其相似,要不我叫它“翻栏”?会不会更容易理解一点哈哈哈
因此我们第一步要做的就是获取当前栏目的下一个栏目的链接,搞清楚了“翻栏”,接下来实现切换不同栏目,也就是一句循环解决的问题,这次还是while循环吗?
不,宝,你没发现我们可以获取到一个存着所有栏目及栏目链接的li列表吗?我们直接使用for循环遍历li列表,然后获取栏目名称及栏目链接,之后根据栏目链接开始之前两步的过程,但是宝,你要注意:
行业新闻的栏目下没有任何新闻,如果还进行之前两步的过程就会解析出错,所以在开始爬取所有栏目下的新闻前,我们要判断一下当前栏目是否为行业新闻栏目,如果是,就跳过进行下一次循环,
并且我们在存储时也可以玩的花哨一点哈哈哈,使用栏目名称来命名,分门别类的存储不同栏目的新闻信息。所以,宝,现在我们开始网页调试叭:
调试观察可知:
① 所有的栏目都包裹在一个class属性为cuhksz-news-column-title-list
的ul
标签里
② 每一个栏目的链接包裹在一个ul
标签下的li
标签里,且当前栏目会有class属性为active
,而其它栏目则没有该属性
③ 根据网页展示以及②中当前栏目不同于其它栏目的热性可知,最后一个栏目是校友动态
,因此,要想知道当前栏目是否为最后一个栏目,只需判断class=active
的li
标签里的链接文本内容是否为校友动态
即可
至此,我们实现自动切换不同栏目的思路就基本清晰了:
1> 从获取的页面整体内容内查找并获取class="cuhksz-news-column-title-list"的ul标签下的内容
2> 从获取的class="cuhksz-news-column-title-list"的ul标签下的内容内查找并获取data-type="news"的li标签下的a标签里的相对路径的栏目链接
3> 找出拥有class="active"属性的栏目的下一个栏目链接并跳转
4> 使用while循环执行以上步骤,实现自动切换不同栏目,其中 while 循环结束的条件是拥有class="active"属性的li标签下的a标签的文本内容为校友动态
01 代码实现
之前两步我们已经实现了爬取一个栏目所有页新闻的完整功能,现在,我们需要做的是将以上代码整体嵌入进一个用以切换栏目的while循环,代码实现如下:
# 厦门大学新闻爬取
# coding=utf-8
import urllib.request
from urllib.parse import urljoin
from bs4 import BeautifulSoup
conUrl = 'https://alumni.xmu.edu.cn/xwzx/tzgg.htm'
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen(conUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
uls = soup.find_all('ul', {'class': "cuhksz-news-column-title-list"})
lis = uls[0].find_all('li', {'data-type': "news"})
print(lis)
for li in lis:
# 栏目名称
colName = li.findAll('a')[0].text
# 栏目链接
colHref = li.find_all('a')[0].get("href")
baseUrl = "https://alumni.xmu.edu.cn/xwzx/"
colUrl = urljoin(baseUrl, colHref)
# 判断当前栏目是否为‘行业新闻’栏目
if colName != '行业新闻':
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen(colUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
spans = soup.find_all('span', {'class': "p_pages"})
# 获取下一页链接
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
nextPageHref = nextPage[0].find_all('a')[0].get("href")
# 获取尾页(最后一页)链接
lastPage = spans[0].find_all('span', {'class': "p_last p_fun"})
lastPageHref = lastPage[0].find_all('a')[0].get("href")
while conUrl != lastPageHref:
divs = soup.find_all('div', {'class': "tab-content"})
# media-heading
hs = divs[0].find_all('h4')
pageNum = divs[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
with open(colName+'.txt', 'a+', encoding='utf8') as fp:
for h in hs:
url1 = "https://zs.xmu.edu.cn/"
url2 = h.find_all('a')[0].get("href")
# 使用urllib的urljoin()拼接两个地址
# urljoin的第一个参数是基础母站的url, 第二个是需要拼接成绝对路径的url
# 利用urljoin,我们可以将爬取的url的相对路径拼接成绝对路径
url = urljoin(url1, url2)
title = h.findAll('a')[0].text
# 用以提示在爬取中
print(url + ',' + title)
fp.write(url + "," + title + '\n')
# 用以提示爬取第几页结束
print("第" + pageNum + "页爬取结束!!!")
spans = soup.find_all('span', {'class': "p_pages"})
pageNum = spans[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
# 不同栏目的页数不同
if colName == '通知公告':
endPageNum = '3'
elif colName == '总会资讯':
endPageNum = '11'
elif colName == '各地讯息':
endPageNum = '12'
elif colName == '院系新闻':
endPageNum = '4'
elif colName == '校友动态':
endPageNum = '13'
# pageNum 用以识别当前页是否为最后一页
if pageNum != endPageNum:
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
conUrl = nextPage[0].find_all('a')[0].get("href")
# 不同页爬取的下一页链接的缺失的母站url不同
# 用以识别当前页是否为第1页,从而决定需要拼接的基础母站url
if pageNum == '1':
url = "https://alumni.xmu.edu.cn/xwzx/"
else:
url = colUrl[0:35]+'/'
nextPageUrl = urljoin(url, conUrl)
response = urllib.request.urlopen(nextPageUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
else:
break
else:
# 跳过本次循环进行下一次循环
continue
四、完整代码展示
# 厦门大学新闻爬取
# coding=utf-8
import urllib.request
from urllib.parse import urljoin
from bs4 import BeautifulSoup
conUrl = 'https://alumni.xmu.edu.cn/xwzx/tzgg.htm'
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen(conUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
uls = soup.find_all('ul', {'class': "cuhksz-news-column-title-list"})
lis = uls[0].find_all('li', {'data-type': "news"})
print(lis)
for li in lis:
# 栏目名称
colName = li.findAll('a')[0].text
# 栏目链接
colHref = li.find_all('a')[0].get("href")
baseUrl = "https://alumni.xmu.edu.cn/xwzx/"
colUrl = urljoin(baseUrl, colHref)
if colName != '行业新闻':
# 读取URL的HTML代码,输入 URL,输出 html
response = urllib.request.urlopen(colUrl)
content = response.read().decode('utf-8')
# 解析
soup = BeautifulSoup(content, 'html.parser')
spans = soup.find_all('span', {'class': "p_pages"})
# 获取下一页链接
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
nextPageHref = nextPage[0].find_all('a')[0].get("href")
# 获取尾页(最后一页)链接
lastPage = spans[0].find_all('span', {'class': "p_last p_fun"})
lastPageHref = lastPage[0].find_all('a')[0].get("href")
while conUrl != lastPageHref:
divs = soup.find_all('div', {'class': "tab-content"})
# media-heading
hs = divs[0].find_all('h4')
pageNum = divs[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
with open(colName+'.txt', 'a+', encoding='utf8') as fp:
for h in hs:
url1 = "https://zs.xmu.edu.cn/"
url2 = h.find_all('a')[0].get("href")
# 使用urllib的urljoin()拼接两个地址
# urljoin的第一个参数是基础母站的url, 第二个是需要拼接成绝对路径的url
# 利用urljoin,我们可以将爬取的url的相对路径拼接成绝对路径
url = urljoin(url1, url2)
title = h.findAll('a')[0].text
# 用以提示在爬取中
print(url + ',' + title)
fp.write(url + "," + title + '\n')
# 用以提示爬取第几页结束
print("第" + pageNum + "页爬取结束!!!")
spans = soup.find_all('span', {'class': "p_pages"})
pageNum = spans[0].find_all('span', {'class': "p_no_d"})
pageNum = pageNum[0].text
# 不同栏目的页数不同
if colName == '通知公告':
endPageNum = '3'
elif colName == '总会资讯':
endPageNum = '11'
elif colName == '各地讯息':
endPageNum = '12'
elif colName == '院系新闻':
endPageNum = '4'
elif colName == '校友动态':
endPageNum = '13'
# pageNum 用以识别当前页是否为最后一页
if pageNum != endPageNum:
nextPage = spans[0].find_all('span', {'class': "p_next p_fun"})
conUrl = nextPage[0].find_all('a')[0].get("href")
# 不同页爬取的下一页链接的缺失的母站url不同
# 用以识别当前页是否为第1页,从而决定需要拼接的基础母站url
if pageNum == '1':
url = "https://alumni.xmu.edu.cn/xwzx/"
else:
url = colUrl[0:35]+'/'
nextPageUrl = urljoin(url, conUrl)
response = urllib.request.urlopen(nextPageUrl)
content = response.read().decode('utf-8')
# https://alumni.xmu.edu.cn/xwzx/tzgg/2.htm
# 解析
soup = BeautifulSoup(content, 'html.parser')
else:
break
else:
continue
五、小结
至此,我们按照之前的思路同步的去爬了厦门大学官网的新闻信息,当然,代码上有一些细节的改动,这是不可避免的,因为不同网站设计也不同,但是掌握这整个思路,基本上是可以通用的,还是那句,静态网站比较简单,各种细节经过调试也容易发现…
好吧,宝,今天我不想写总结了,有问题或不理解的私信或评论我都可!
(词频统计还有其他博文之后会更)