爬虫实践爬取官方新闻标题正文时间
Posted zstar-_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了爬虫实践爬取官方新闻标题正文时间相关的知识,希望对你有一定的参考价值。
爬虫用的频率较少,每次使用都会手生,特此记录一次实战经历。
项目需求
要求爬取济南市政务网中“滚动预警”菜单中的文章,包括文章标题,文章正文,文章时间,并保存为txt文件。
项目分析
1、判断可爬取的内容
首先查看该网站的robots.txt文件,发现并不存在该文件。因此相关公开信息可正常爬取。
2、确定网页的加载模式
网页加载可分为静态加载和动态加载。
在网页中右键->选择查看源代码,即网页的静态代码。在网页中右键->检查,可查看浏览器当前渲染的内容。
若两者一致,则网页是静态加载。此时,通常使用requests.get的方式即可获取到网页数据。
若两者不一致,网页是动态加载。此时需通过开发者后台查看本地向服务器发送的交互性数据(XHR)。
每隔3页,网页会短暂卡顿,进行加载,同时可以发现多了一份XHR数据,如图所示,此时,请求的网址如上所示,url中标记了起始数据和结束数据,同时,网页的请求方法为POST。
3、查看提交的表单内容
如图所示,提交的表单主要有七个数据,查看该网站的其他页面,可以大概猜测:
webid用于区分不同的大板块,columnid用于区分每个大板块中的小板块,其它属性未知。在翻页过程中,仅有url发生变化,提交的表单内容固定。
还可以发现,应急要闻和滑动预警所请求的url相同,区别是表单数据:应急要闻的columnid为29112,滑动预警的columnid为34053。
4、获取文章标题、内容与发布时间
通过上面的分析,已经可以通过post的方式获取各页目录的源代码,再次基础上,需要通过目录的链接,进入到每篇文章的页面进行,标题、正文、时间的提取。
通过bs4的函数工具以及正则表达式,可将链接内容提取出来,存放到Linklist。
点击链接进行跳转,可以发现正文内容页面使用静态加载,此时使用get或post的方法均可得到文章内容。我在这里仍沿用了之前封装好的post方法。
分别提取文章标题、内容、时间存储至title_list、content_list、time_list。
5、寻找规律自动翻页
通过上面的操作,已经可以获取了一次加载的内容,即三页内容(27条新闻),下面将通过寻找规律进行多次加载。
寻找规律:
1-3页:
http://jnsafety.jinan.gov.cn/module/web/jpage/dataproxy.jsp?startrecord=1&endrecord=27&perpage=9
4-6页:
http://jnsafety.jinan.gov.cn/module/web/jpage/dataproxy.jsp?startrecord=28&endrecord=54&perpage=9
255页(最后一页):
http://jnsafety.jinan.gov.cn/module/web/jpage/dataproxy.jsp?startrecord=1999&endrecord=2025&perpage=9
发现仅是变化了startrecord(开始页面)和endrecord(结束页面)
于是设定开始页面为i=1,结束页面为i+26,每次遍历i+27,直到返回的Linklist为空,跳出循环。
完整代码
import os
from bs4 import BeautifulSoup
import re
import requests
# post得到网页并用bs4进行网页解析
def gethtml(url):
header = 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/52.0.2743.116 Safari/537.36'
data =
'col': 1,
'webid': '33',
'path': 'http://jnsafety.jinan.gov.cn/',
# 'columnid': '29112', # 29112对应应急要闻
'columnid': '34053', # 34053对应滚动预警
'sourceContentType': '1',
'unitid': '92715',
'webname': '%E6%B5%8E%E5%8D%97%E5%B8%82%E5%BA%94%E6%80%A5%E7%AE%A1%E7%90%86%E5%B1%80',
'permissiontype': 0
rep = requests.post(url=url, data=data, headers=header, stream=True).text
# 解析网页
soup = BeautifulSoup(rep, 'html.parser')
return soup
# 从a标签中切分出具体文章链接
def split_link(string):
start_string = 'http'
end_string = '.html'
sub_str = ""
start = string.find(start_string)
# 只要start不等于-1,说明找到了http
while start != -1:
# 找结束的位置
end = string.find(end_string, start)
# 截取字符串 结束位置=结束字符串的开始位置+结束字符串的长度
sub_str = string[start:end + len(end_string)]
# 找下一个开始的位置
# 如果没有下一个开始的位置,结束循环
start = string.find(start_string, end)
return sub_str
# 截取文章发布时间的年月日
def split_time(t):
year = t[0:4]
month = t[5:7]
day = t[8:10]
data = "%s-%s-%s" % (year, month, day)
return data
# 获取一页中的所有链接
def get_link_list(soup):
# 使用正则表达式提取链接内容
p = re.compile(r'<div class="ajj_clear_box">(.*?)</div>?', re.S)
items = re.findall(p, str(soup))
# print(items)
Linklist = []
# 返回出各网站内容链接
for item in items:
# print(item)
link = split_link(item)
Linklist.append(link)
return Linklist
# 获取单篇文章标题、内容与发布时间
def get_title_content(soup_ev):
# 文章标题
title = soup_ev.find(name="meta", attrs="name": "ArticleTitle")['content']
# print(title)
# 文章内容
content = soup_ev.find(name="div", attrs="id": "zoom").findAll(name="span")
# 文章发布时间
pub_time = soup_ev.find(name="meta", attrs="name": "pubdate")['content']
p_time = split_time(pub_time)
# print(p_time)
return title, content, p_time
# 保存单篇新闻
def save_content(title, content, index, time):
for item in content:
text_content = item.text
# print(text_content)
# 以标题名作为文件名,防止某些标题含有特殊符号,将其替换为空
sets = ['/', '\\\\', ':', '*', '?', '"', '<', '>', '|']
for char in title:
if char in sets:
title = title.replace(char, '')
tex_name = "%d%s-%s" % (index, title, time)
# 注:由于每段文字是分离的,因此写入文件模式设定为追加写入(a)
# 文件夹在主函数内创建
with open(r'./应急要闻/%s.txt' % tex_name, mode='a', encoding='utf-8') as f:
# 每段文字进行换行
f.write(text_content + "\\n")
''' 滚动预警
with open(r'./滚动预警/%s.txt' % tex_name, mode='a', encoding='utf-8') as f:
# 每段文字进行换行
f.write(text_content + "\\n")
'''
# 获取一次加载的新闻链接列表
def get_news_list(Linklist):
title_list = []
content_list = []
time_list = []
for item in Linklist:
# item、soup_ev都有可能因返回数据出现异常中断,这里对异常数据不作处理,跳过中断
try:
soup_ev = getHtml(item)
title, content, p_time = get_title_content(soup_ev)
title_list.append(title)
content_list.append(content)
time_list.append(p_time)
except Exception:
pass
continue
return title_list, content_list, time_list
# 根据文章的时间重新进行排序(按时间从后到前)
def sort_news(title_list, content_list, time_list):
title_content_time = zip(title_list, content_list, time_list)
sorted_title_content_time = sorted(title_content_time, key=lambda x: x[2], reverse=True)
result = zip(*sorted_title_content_time)
title_list, content_list, time_list = [list(x) for x in result]
return title_list, content_list, time_list
# 保存list中所有新闻
def save_all(title_list, content_list, time_list):
loop = zip(title_list, content_list, time_list)
index = 1
for title, content, time in loop:
save_content(title, content, index, time)
index += 1
if __name__ == '__main__':
# 在当前目录下创建存储新闻内容的文件夹
path = os.getcwd()
file_path = path + '\\\\' + str("滚动预警")
# file_path = path + '\\\\' + str("应急要闻")
os.mkdir(file_path)
# 存储每三页的标题、内容、时间
title_list = []
content_list = []
time_list = []
# 存储所有新闻的标题、内容、时间
tol_title_list = []
tol_content_list = []
tol_time_list = []
i = 1
while True:
url = 'http://jnsafety.jinan.gov.cn/module/web/jpage/dataproxy.jsp?startrecord=%d&endrecord=%d&perpage=9' % (i, i + 26)
soup = getHtml(url)
Linklist = get_link_list(soup)
# 取消下面的注释,可打印出每次请求得到的链接数,以显示程序正在允许中
# print(len(Linklist))
# print(Linklist)
# 假如爬完所有内容,跳出循环
if Linklist:
title_list, content_list, time_list = get_news_list(Linklist)
tol_title_list.extend(title_list)
tol_content_list.extend(content_list)
tol_time_list.extend(time_list)
else:
break
i = i + 27
# print(len(tol_title_list))
# print(len(tol_content_list))
# print(len(tol_time_list))
tol_title_list, tol_content_list, tol_time_list = sort_news(tol_title_list, tol_content_list, tol_time_list)
save_all(tol_title_list, tol_content_list, tol_time_list)
常见报错
1、(‘Connection aborted.’, TimeoutError(10060, ‘由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。’, None, 10060, None))
解决方式:关闭电脑的防火墙。
2、Failed to establish a new connection: [WinError 10060] 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。’))
问题分析:该报错可能是ip被封或者爬虫访问速度过快,服务器来不及响应。
解决方式:每次gethtml添加time.sleep(1),让每次爬取间隔1秒时间,若仍报错,尝试使用代理ip。
以上是关于爬虫实践爬取官方新闻标题正文时间的主要内容,如果未能解决你的问题,请参考以下文章
爬虫智能解析库 Readability 和 Newspaper 的用法