Python 的简单爬取天气
最近做个简单小项目需要一些天气数据值,所以想去网上爬取一些。下面是我的爬取案例。使用的网站是2345天气网。
首先我们分析下网站。打开网页按住F12则可以看到网页的结构。如下图。
但当我们改变年份和月份时候发现网页没有发生变化,那我们可以考虑我们需要的东西肯定是动态加载的。
当我们看network里相应发下,改变年份或者月份出来一个响应,我们点开看看。发现header的URL是 http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=57036&areaInfo%5BareaType%5D=2&date%5Byear%5D=2020&date%5Bmonth%5D=2
后面的2020和2 和我们的选的年份和月份。并发现响应的返回类型是json。
这是返回的数据。
我们在json格式化下发现我们需要的数据在data里
ok,前期工作我们做完了,就开始写代码吧。我用xpath进行数据解析,当然你喜欢的话可以用bs4或者正则。我觉得xpath比较简单快捷,感觉xpath类似css的选择器。下面讲下xpath的简单应用。
1.xpath 解析原理
- 实例化一个etree对象,且需要将解析到页面源码加载到该对象中
- 调用etree对象的xpath方法结合着xpath表达式实现标签的定位和内容的捕获
- 如何实例化一个etree对象:import lxml.etree
- 将本地源码html加载到etree对象
etree.parse(filepath) - 可以将网上请求的源码页面加载到该对象中
etree.HTML(‘page_text’)
xpath(‘xpath表达式’)
- 可以将网上请求的源码页面加载到该对象中
- 将本地源码html加载到etree对象
2.xpath表达式:
- /:表示从根节点开始定位。表示一个层级.
- //:表示多个层级。可以任意位置定位
- 属性定位: //div[@class=‘song’] tag[@attrName=‘attr’]
- 索引定位: //div[@class=‘song’]/p[3] 索引是从1开始的
3.取文本
- /text() 获取直系标签的文本内容
- //text() 获得非直系的文本内容(所以的文本内容)
- 取属性:
- /@attrName ==>> img/@src
代码
import requests
from lxml import etree
def get_table(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36'
}
# 请求数据 并获得返回的json数据
text_json = requests.get(url=url, headers=headers).json()
etree_html = etree.HTML(text_json['data'])
# 利用xpath获得所有tr标签
html_xpath = etree_html.xpath('.//tr')[1:]
data = []
for ls in html_xpath:
# tr标签的所有值
xpath = ls.xpath('.//text()')
# 进行简单数据处理
xpath = list(map(lambda x: x.strip(), xpath))
xpath = [x for x in xpath if len(x) != 0]
data.append(xpath)
return data
if __name__ == '__main__':
url = 'http://tianqi.2345.com/Pc/GetHistory?areaInfo%5BareaId%5D=57036&areaInfo%5BareaType%5D=2&date%5Byear%5D={}&date%5Bmonth%5D={}'
# 将2012 年到2019年 1- 12 月的所有数据存储到本地的csv文件
with open(r'C:\Users\YD\Desktop\xian.csv', 'a') as file:
for year in range(2012, 2020, 1):
for month in range(1, 13, 1):
data = get_table(url.format(year, month))
for ls in data:
for day in ls:
file.write(day)
file.write(',')
file.write('\n')