主要模块
- requests模块。使用requests模块来获取http响应
- gevent模块。使用gevent开启多个协程,加快爬取速度
- re模块或beautifulsoup模块。正则表达式解析与beautifulsoup解析两种解析方式我都会写出来。
- csv模块。用于将数据导出至csv文件内
分析过程
1.要爬取的页面的URL地址为:http://www.tianqihoubao.com/aqi/。首先访问该页面(如下图)获取所有城市a标签的href属性,知道了各个城市的api,就可以接着爬取每个城市具体的空气质量了。
2.打开chrome的调试页面,可以看到,a标签在dd标签下,而且整个页面,只有dd标签下只要a标签没有其他标签了。所以正则匹配或使用BS4查找元素时,都可以先找dd标签,再找下面的a标签的href属性。(整个页面只有这里有dd标签,所以要查找dd标签)
3.找到每个城市的url后,再点击去看,发现是月份选择,这里是2020年3月为例,点进去。可以看到,找到了我们需要的空气质量信息。接下来就看如何从页面中将天气信息提取出来。
4.打开chrome调试工具。可以看到,需要的空气质量信息,在tr标签下的td标签内,而且每个tr标签对应一天的空气质量信息,那么就可以首先查找tr标签,然后取出td标签内的空气质量信息。(整个页面只有这里有tr标签,所以要查找tr标签)
代码如下
1.先写一下使用re正则解析的完整代码。我的思路是:先获取所有城市的a标签中的href属性,然后在URL最后拼接出想获取的月份,这里我获取的是2020年前3个月的。最后开启10个协程,对每个城市新建一个文件,写入抓取的空气质量数据。如果需要将所有文件合成一个的话,可以执行我单列出来的那段代码。
import time
import csv
import re
import gevent
from gevent import monkey,pool
monkey.patch_all()
import requests
# 存储城市url
city_url = []
# 存储要查看的时间范围
weather_date = []
# task列表
task_list = []
def func(url):
"""获取html页面"""
response = requests.get(url)
# 判断是否请求成功
if response.status_code != 200:
print("请求失败")
print(response.headers)
return
html = response.content.decode("gbk")
response.close()
return html
def get_city_url_list(url):
"""获取城市url列表"""
try:
html = func(url)
except Exception as e:
return
city_list = re.findall(r"<dd>(.*?)</dd>", html, re.S)
for i in city_list:
# cities = re.findall(r'<a href="(.*?)"\s?>', i)
cities = re.findall(r'<a href="(.*?)"\s?>(.*?)</a>', i)
for j in cities:
# city_url.append('http://www.tianqihoubao.com' + j)
city_url.append(j)
def get_day_weather_data(urlname):
"""获取每个城市的2019全年与2020年之间的天气情况,并存储为csv文件
urlname:tuple,第一项为url,第二项为name
f:文件描述符
"""
with open(r".\2020年1-3月\%s.csv" % urlname[1], "w", newline='') as f:
name = urlname[1]
for i in weather_date:
new_url = re.sub(r"\.html", "-"+i+".html", urlname[0])
print(new_url)
html = func('http://www.tianqihoubao.com' + new_url)
# 失败的话尝试3次
# times = 0
# while times < 3:
# try:
# html = func('http://www.tianqihoubao.com' + new_url)
# break
# except Exception as e:
# time.sleep(2)
# times += 1
# if times == 3:
# return
row_list = re.findall(r"<tr>(.*?)</tr>", html, re.S)
for j in row_list[1:]:
aqi_data = re.findall(r"<td.*?>\s*(.*?)\s*</td>", j, re.S)
# 2. 基于文件对象构建 csv写入对象
csv_writer = csv.writer(f)
csv_writer.writerow([name]+aqi_data)
if __name__ == "__main__":
get_city_url_list("http://www.tianqihoubao.com/aqi/")
# print(city_url)
# for i in city_url[41:]:
# print(i)
# 处理标签变为201903这种格式
date_list = [str(i) for i in range(1, 4)]
for i in date_list:
if len(i) < 2:
i = "0" + i
weather_date.append("2020"+i)
# print(weather_date)
# with open(r"全国城市天气2.csv", "w") as f:
# for i in city_url:
# get_day_weather_data(i, f)
# 多协程生成csv文件
task_pool = pool.Pool(10)
for i in city_url:
task_pool.apply_async(get_day_weather_data, args=(i,))
task_pool.join()
import os
# 如果需要将所有文件合成为一个的话,执行这段代码
path = 'D:\\Pycharm Projects\\天气数据爬取\\2020年1-3月'
pathnames = []
for (dirpath, dirnames, filenames) in os.walk(path):
for filename in filenames:
print()
pathnames += [os.path.join(path, filename)]
print(pathnames)
with open(r".\weather_data_202001-03.csv", "w") as f:
for i in pathnames:
with open(i, "r") as g:
data = g.read()
f.write(data)
2.后来我又学习了一下beautulsoup模块,解析html页面很简单,我写的抓取a标签href属性部分的代码如下:
# http://www.tianqihoubao.com/aqi/
with open("123.html", "r") as f:
soup = BeautifulSoup(f, "html.parser")
# 找到所有dd标签
l1 = soup.find_all('dd')
for i in l1:
# 找到每个dd标签下的所有a标签
l2 = i.find_all('a')
for j in l2:
# 打印出每个a标签的href属性
print(j['href'])
3.最后说下我的感受。关于re与beautifulsoup两个模块,我更习惯于用re模块,re给我的感觉就是:虽然写起来有点难度,但是抓数据时想抓哪里就抓哪里,比如我上面匹配到的数据中间有空格换行等,使用正则表达式可以在抓取的时候就将这些剔除掉,而beautifulsoup我目前还没有找到比较好的方法。大家可以自行尝试,对代码部分有疑问可以直接在文章下面评论,我看到后会立即回复。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。