赞
踩
最近有个需求,爬取文章数据供大屏端使用。
菜鸡落泪,记录一下学习过程与踩过的坑
我选择爬取的网站是云南省应急管理厅的数据url为:云南省应急管理厅 (yn.gov.cn),选取里安全生产的综合监管标题栏下的文章爬取如下:
导入所需要的函数库 后从创建列表用于存放数据如下:
- '''导入相关库'''
- from lxml import etree #解析文档
- import bs4
- import requests #获取网页
- import pandas as pd #保存文件
- '''构造循环爬取网页'''
- all_content = [] #内容
- all_title = [] #爬取的标题存储在列表中
- all_time = [] #爬取的时间存储在列表中
- url = [] #研究报告网页链接
- href = []
然后设置用户代理,获取该目录页面的html超文本标记语言,定位到每篇文章标题
- headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
- }#构造头文件,这是模拟真人登录(ps:有缺失,请自己动手)
- url1 = f'http://yjglt.yn.gov.cn/html/anquanshengchan/zonghejianguan/'#目标网页链接
- res1 = requests.get(url1,headers=headers)#获取网页
- soup = bs4.BeautifulSoup(res1.text,"html.parser")#解析
- targets1 = soup.find_all("li",class_="list-group-item")#获取目标字段(ps:暂时叫它字段吧),见下面第一张图(图1,依次类推)
- #print(len(targets1))
- #print(targets1)
接下来就是仔细观察到每篇文章的链接不同,有的以http开头,有的以html开头,用一个if条件判断将其区分开:
然后就是进入每篇文章的链接,爬取文章的标题、发布时间、文章内容保存于列表,xpath定位相关内容,值得一提的是下面只要时间不要出处,可将xpth中的span改为span[1]。
还有就是html开头的链接无效,需要将url补充完整如下:
如此便成功爬取到了数据,将时间列表规范一下,搭建DataFrame对象保存数据为csv文件,代码和文件截图如下:
- #爬取的时间列表为双列表,此处用此操作转换为单列表
- all_time = [item for sublist in all_time for item in sublist]
- #创建DataFrame对象存放数据
- data_raw = pd.DataFrame()
- data_raw["puth_time"] = all_time
- data_raw["title"] = all_title
- data_raw["content"] = all_content
- #生成CSV文件
- data_raw.to_excel("安全生产.xls",index=False,encoding='gbk')#这里我就不放保存路径了,每个人的都不一样
- print("安全生产保存成功!")
爬取得到的数据不是很规范,前面的中括号和换行符特别影响心情,需要把它简单处理,后面的在页面端一般看不到,不做处理,这里我使用python列表切片法,爬取下来的列表切片后为空,查看字符串长度为2,这么多字这么长怎么可能为2?在保存为csv文件后再读取,发现字符串长度变正常了,比title字段,长度大概在90多,直接对字段title和content前面内容切片
- #读取文件并将字段类型设置为字符型,如果不保存为CSV文件,许多中文内容字符串长度为2,不方便切片处理,保存之后字符串长度变长,蛮方便处理
- data = pd.read_excel('安全生产.xls',dtype = 'str')
- #data.head()
- #切片处理标题字段和内容字段,将前面的杂乱字符剔除,后面的和中间的多余字符没咋管
- title_extra = []
- content_extra = []
- for i in data["title"]:
- #print(i[5:])
- title_extra.append(i[7:70])
- for i in data["content"]:
- #print(i[8:])
- content_extra.append(i[8:])
切片后保存为新的csv文件。
因为是小白,之前只接触过一次(抄室友代码),现在自己来入库也是比较曲折,在成功连接自己数据库的基础上,用游标自己写SQL插入多行数据,for循环+execute函数,失败!,直接用executemany函数一次插入多行,也是失败,报错大概意思是字符串占位符不太匹配,要么就是SQL有语法错误。卡了半天没解决,插入部分代码如下:
- # 3). *********************插入多条数据****************************
- try:
- info_1 = all_date_and_resouce
- info_2 = all_title
- info_3 = all_content
-
- for i in all_title:
- print(i)
- insert_sqli = "insert into 舆情(title) values(i);"
- cur.execute(insert_sqli)
-
- insert_sqli_1 = "insert into 舆情(`time`) values(%s);"
- insert_sqli_2 = "insert into 舆情(title) values(%s);"
- insert_sqli_3 = "insert into 舆情('content') values(%s);"
- cur.executemany(insert_sqli_1, info_1)
- cur.executemany(insert_sqli_1, info_1)
- cur.executemany(insert_sqli_3, info_3)
- except Exception as e:
- print("插入多条数据失败:", e)
- else:
- # 如果是插入数据, 一定要提交数据, 不然数据库中找不到要插入的数据;
- conn.commit()
- print("插入多条数据成功;")
后来在博客看到一个方法,不用连上数据库手动写SQL,直接将DataFrame数据插入数据库,两行代码完全解决问题!!!
- #读取处理后的数据,为导入mqsql做准备
- data_final = pd.read_excel('安全生产_final.xls',dtype = 'str')
- data_final.head()
- #导入相关库
- from sqlalchemy import create_engine
- #配置mqsql
- engine = create_engine("mysql+pymysql://root:123@localhost:3306/db1?charset=utf8mb4")
- #导入mysql数据库
- data_final.to_sql(name="舆情1",con=engine,if_exists='replace',index=False,index_label=False)
- print('插入数据库成功!')
看一下数据库
OK,插入成功!
1.爬取数据还得精准定位url,不然数据都找不到。
2.爬虫定位具体数据,re、bs4、xpath三种方式很灵活,用法也很多,处理得当更容易获取规范的数据
3.爬虫还得懂一部分前端知识,
4.python的数据类型,python的双重列表和列表数据提取也挺灵活,有时可以转换为DataFrame用pandas来处理。
5.python中的连接数据库写SQL类似Java的传统JDBC操作,有什么游标啥的,而后面python采用sqlalchemy函数库(第一次见这个东东)一句代码就将DataFrame数据直接插入,感觉比Java的Mybatis框架还NB,只能说我还是个弟弟。
- '''导入相关库'''
- from lxml import etree #解析文档
- import bs4
- import requests #获取网页
- import pandas as pd #保存文件
- '''构造循环爬取网页'''
- all_content = [] #内容
- all_title = [] #爬取的标题存储在列表中
- all_time = [] #爬取的时间存储在列表中
- url = [] #研究报告网页链接
- href = []
-
- headers = {
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36'
- }#构造头文件,这是模拟真人登录(ps:有缺失,请自己动手)
- url1 = f'http://yjglt.yn.gov.cn/html/anquanshengchan/zonghejianguan/'#目标网页链接
- res1 = requests.get(url1,headers=headers)#获取网页
- soup = bs4.BeautifulSoup(res1.text,"html.parser")#解析
- targets1 = soup.find_all("li",class_="list-group-item")#获取目标字段(ps:暂时叫它字段吧),见下面第一张图(图1,依次类推)
- #print(len(targets1))
- #print(targets1)
-
- #判断文章的URL是以html开头还是http开头,如果是以html开头则需要下文进行处理变为标准的URL,才能准确定位到文章
- targets_html =[]
- targets_http = []
- for x in targets1:
- y = x.a["href"].startswith("/html")
- if y == True:
- targets_html.append(x)
- else:
- targets_http.append(x)
- print("html为前缀的文章为:***********************************************************")
- print(targets_html)
- print("http为前缀的文章为:***********************************************************")
- print(targets_http)
-
- #解析文章的内容,通过xpath方法定位到文章的标题、发布时间、文章内容,从而进行爬取
- for e in targets_http:
- h2=e.a["href"]#获取href里的内容
- res3 = requests.get(h2,headers=headers)#解析href的网页链接(即文本链接)
- root2 = etree.HTML(res3.text)#再次解析href链接里的内容
- '''接下来就是在里面获取内容,后面的步骤与上一篇文章的方法差不多,就不过多赘述啦'''
- title1 = root2.xpath("/html/body/div[2]/div[2]/div[2]/div/div[1]/text()")
- #print(title)
- #提取span[1]就仅仅提取到时间,而没有提取到来源和出处
- date_and_resouce1= root2.xpath("//div[contains(@class,'info-container')]//div[contains(@class,'info-title')]//div[contains(@class,'report-subtitle detail-subtitle')]//span[1]//text()")
- #source = root.xpath("//div[contains(@class,'content')]//div[@class='creab']//span//a//text()")
- #print(company)
- content3 = root2.xpath("//div[contains(@class,'info-container')]//div[contains(@class,'info-content')]//p//text()")
- #文章内容存在着分割,图片或者表格分割,他在HTML语言中分段存放,所以要将文章内容合并
- content4 = "".join(content3)
- #print(content2)
- #在定义好的总列表添加爬取到的的相关内容
- all_title.append(title1)
- all_time.append(date_and_resouce1)
- all_content.append(content3)
- print("第一部分数据爬取完成!")
- #html语言开头的URL不规范,此处在其前面添加'http://yjglt.yn.gov.cn/'方能精准定位,其余操作与http开头的url一样
- for each in targets_html:
- hl=each.a["href"]#获取href里的内容
- res2 = requests.get('http://yjglt.yn.gov.cn/'+hl,headers=headers)#解析href的网页链接(即文本链接)见图2
- root = etree.HTML(res2.text)#再次解析href链接里的内容
- '''接下来就是在里面获取内容,后面的步骤与上一篇文章的方法差不多,就不过多赘述啦'''
- title = root.xpath("/html/body/div[2]/div[2]/div[2]/div/div[1]/text()")
- #/html/body/div[2]/div[2]/div[2]/div/div[1]/text()
- #print(title)
- date_and_resouce= root.xpath("//div[contains(@class,'info-container')]//div[contains(@class,'info-title')]//div[contains(@class,'report-subtitle detail-subtitle')]//span[1]//text()")
- #source = root.xpath("//div[contains(@class,'content')]//div[@class='creab']//span//a//text()")
- #print(company)
- content1 = root.xpath("//div[contains(@class,'info-container')]//div[contains(@class,'info-content')]//p//text()")
- content2 = "".join(content1)
- #print(content2)
- all_title.append(title)
- all_time.append(date_and_resouce)
- all_content.append(content2)
- #print(all_title)
- #print(all_company)
- print("第二部分数据爬取完成!")
-
- #爬取的时间列表为双列表,此处用此操作转换为单列表
- all_time = [item for sublist in all_time for item in sublist]
- #创建DataFrame对象存放数据
- data_raw = pd.DataFrame()
- data_raw["puth_time"] = all_time
- data_raw["title"] = all_title
- data_raw["content"] = all_content
- #生成CSV文件
- data_raw.to_excel("安全生产.xls",index=False,encoding='gbk')#这里我就不放保存路径了,每个人的都不一样
- print("安全生产保存成功!")
-
- #读取文件并将字段类型设置为字符型,如果不保存为CSV文件,许多中文内容字符串长度为2,不方便切片处理,保存之后字符串长度变长,蛮方便处理
- data = pd.read_excel('安全生产.xls',dtype = 'str')
- #data.head()
- #切片处理标题字段和内容字段,将前面的杂乱字符剔除,后面的和中间的多余字符没咋管
- title_extra = []
- content_extra = []
- for i in data["title"]:
- #print(i[5:])
- title_extra.append(i[7:70])
- for i in data["content"]:
- #print(i[8:])
- content_extra.append(i[8:])
- #content_extra
- #将处理后的数据保存为CSV文件
- data_raw["puth_time"] = all_time
- data_raw["title"] = title_extra
- data_raw["content"] = content_extra
- data_raw.to_excel("安全生产_final.xls",index=False,encoding='gbk')#这里我就不放保存路径了,每个人的都不一样
- print("安全生产_final保存成功!")
-
- #读取处理后的数据,为导入mqsql做准备
- data_final = pd.read_excel('安全生产_final.xls',dtype = 'str')
- data_final.head()
- #导入相关库
- from sqlalchemy import create_engine
- #配置mqsql
- engine = create_engine("mysql+pymysql://root:123@localhost:3306/db1?charset=utf8mb4")
- #导入mysql数据库
- data_final.to_sql(name="舆情1",con=engine,if_exists='replace',index=False,index_label=False)
- print('插入数据库成功!')
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。