标题:python 爬取机动车国环公布信息(一)
摘要:根据循环网页(目录页)的页数,确认有规律的网址url,遍历目录页,筛选目标页(公布信息)的title(日期) 和网址信息url。然后再遍历进入目标页网址,抓取网页中信息公布数据。细节请看过程分析及代码解释。
功能要求
获取机动车国环历次公布数值信息,并导出到表格中,以便整理并做出趋势图。
工具语言
python 3.7 IDE:PyCharm,以及部分模块库
分析:
“”"
国六车型国环信息爬虫系统
url:主页
https://www.vecc.org.cn/
url:目录页(有规律)
https://www.vecc.org.cn/tzgg/index.jhtml
https://www.vecc.org.cn/tzgg/index_2.jhtml
https://www.vecc.org.cn/tzgg/index_3.jhtml
https://www.vecc.org.cn/tzgg/index_4.jhtml
https://www.vecc.org.cn/tzgg/index_5.jhtml
https://www.vecc.org.cn/tzgg/index_6.jhtml
https://www.vecc.org.cn/tzgg/index_7.jhtml
https://www.vecc.org.cn/tzgg/index_8.jhtml
https://www.vecc.org.cn/tzgg/index_9.jhtml
…
url:目标页(无规律)
https://www.vecc.org.cn/tzggxxgk/3328.jhtml
https://www.vecc.org.cn/tzggxxgk/3318.jhtml
…
第一步:爬取目录页
方法一:进入 https://www.vecc.org.cn/ 网址,爬取上方0-9(。。。)目录页网址信息,并将子网址信息保存的列表中
方法二:优于目录页网址有规律,即可直接设定,无需再从网页上爬取。
第二步:从各目录页爬取目标页网址信息
从爬取的目标页网址信息中再次使用爬虫系统(设置循环),进一步爬取详细信息
第三步:数据保存
数据保存,CSV格式
第四步:数据处理
数据处理,并输出折线图,请看python 爬取机动车国环公布信息(二)
网址:https://blog.csdn.net/weixin_44816589/article/details/103962499
“”"
罗里吧嗦一堆,接着就上代码!
import requests
from bs4 import BeautifulSoup
import re
import csv
def get_targets_pages_address(num):
'''
获取目录页网址信息
由于目录页有规律可循,因此跨过主页而直接进入目录页,以爬取目标页网址
结构化的网址——露出会心从笑容
'''
try: # 本人现在超级喜欢使用try结构体
if num == 1:
url = 'https://www.vecc.org.cn/tzgg/index.jhtml'
elif num > 1:
# url = 'https://www.vecc.org.cn/tzgg/index_' + str(num) + '.jhtml'
url = 'https://www.vecc.org.cn/tzgg/index_{}.jhtml'.format(str(num))
else:
print('超出范围')
r = requests.get(url, timeout=30) # timeout 设置请求时间,主要为防止服务器访问超时,解决代码卡在某一位置若干分钟甚至更长时间,既不报错也没有request请求响应。我们这里设置为30s
soup = BeautifulSoup(r.text, 'lxml')
# 找出class属性值为list的div
targets_list = soup.find_all('div', {'class': 'list'})
targets = []
addresses = []
for i in targets_list: # 遍历
target = i.find('span').find('a').get('title') # 获取title
address = i.find('span').find('a').get("href") # 获取href
if target[:3] == "国六车": # 根据title中前三个字符是否为“国六车”来筛选相关的title及网址address
targets.append(target)
addresses.append(address)
else:
pass
# print(targets)
# print(len(targets))
# print(addresses)
# print(len(addresses))
except:
pass # 由于比较简单,出错后直接跳过,不做处理
return targets+addresses # 将两个长度相等的列表相加作为一个列表返回,调用函数时再将两者从中间进行切片
def get_vecc_information(url_list): # 获取 轻型车:--- 字段
"""
进入目标页,以获取国环公开信息
如果把上一个函数看作是第一层爬虫,那么这个函数就是第二层爬虫
:param url_list:
:return:
"""
try:
url = url_list
r = requests.get(url, timeout=30)
soup = BeautifulSoup(r.text, 'lxml')
# 找出class属性值为list的div
text_list = soup.find_all('div', {'class': 'content-info-box'})
# print(text_list)
text = str(text_list) # 将text_转化为字符串格式
''' 按多字符分割,保存为列表 '''
text_split_list = re.split('[<p> <br /> > \xa0]', text)
print("----------------------------")
for i in text_split_list: # 判断列表元素i是否是以“轻型车”开头,如果是,赋值给target_text
if i.startswith('轻型车'):
target_text = i
except:
pass
return target_text # 返回target_text
def num_get(string): # 从字符串中提取出数值
"""
可参照:https://blog.csdn.net/weixin_44816589/article/details/103917063 ;里面涉及了两种提取数字的方法,现取一种
从字符串中提取数值,如企业数、车型数、车辆数
:param string:
:return:
"""
try:
string = string
num_list_new = []
a = ''
for i in string:
if str.isdigit(i):
a += i
else:
a += " "
num_list = a.split(" ")
for i in num_list:
try:
if int(i) > 0:
num_list_new.append(int(i))
else:
pass
except:
pass
return num_list_new
except:
pass
def main():
"""
主函数
数据收取,并存入csv
:return:
"""
n = input("请查看网址 https://www.vecc.org.cn/tzgg/index.jhtml,并输入总页数:____") # 小编再这里偷懒了,如果要做成全自动,可以再设计一层爬虫,从https://www.vecc.org.cn/tzgg/index.jhtml中获取总页数。
targets_temp = [] # 设置临时列表,存放title
address_temp = [] # 设置临时列表,存放网址address
for i in range(1, int(n)): # 结构化的目录页网址
targets_address = get_targets_pages_address(i)
targets_temp += targets_address[:int(0.5*len(targets_address))] # 对获取的targets_address列表从中间分割,前半部分为title
address_temp += targets_address[int(0.5*len(targets_address)):] # 对获取的targets_address列表从中间分割,后半部分为网址address
targets_date_temp = [] # 设置临时列表,存放日期date
for i in range(len(targets_temp)):
targets_date_temp.append(targets_temp[i][17:-1]) # 通过遍历,对字符串进行切片,获取日期date
# print(targets_temp)
# print(targets_date_temp)
# print(address_temp)
targets_date = sorted(targets_date_temp, reverse=False) # 可以手动更改,reverse=True,可反向顺序输出
address = sorted(address_temp, reverse=False) # 可以手动更改,reverse=True,可反向顺序输出
print(targets_date)
print(address) # 打印按正向时间顺序的网址 url
with open('vecc_data_get_file.csv', 'a', newline='') as w: # 打开文件vecc_data_get_file.csv,若无此文件,则进行创建
""" item 为表头,写入到CSV文件第一行 """
item = ["日期", "企业总数", "车型总数", "公开编号个数", "车辆总数", "车型周环比增长", "车辆周环比增长", "国内企业", "国内车型数",
"国内车辆数", "国外企业", "国外车型数", "国外车辆数", "轻型汽油车车企业数", "轻型汽油车型数", "轻型汽油车辆数",
"轻型混动企业数", "轻型混动车型数", "轻型混动车辆数", "轻型柴油企业数", "轻型柴油车型数", "轻型柴油车辆数", "轻型燃气车企业数", "轻型燃气车型数", "轻型燃气车辆数", ]
content = csv.writer(w, dialect='excel')
content.writerow(item)
""" 合并日期date与提取的数值列表进行合并,并写入到CSV文件中"""
for i in range(len(address)):
try:
url_list = address[i]
target_text = get_vecc_information(url_list) # 获取文章段落字符串
# print('target_text is \n', target_text)
num_list = num_get(target_text) # 从文章字符串中提取数字
print(targets_date[i])
print('num_list is \n', num_list)
targets_date_write_in = []
targets_date_write_in.append(targets_date[i]) # 将日期date添加到新的列表,当下只有日期一个元素
if len(num_list) > 0:
content.writerow(targets_date_write_in + num_list) # 将日期列表与数字列表合并,作为一行写入到CSV文件
else:
pass # 判断,如果num_list列表如果为空,则不进行写入,直接跳过
except:
pass
if __name__ == '__main__':
main()
结果:截图示例如下