来源
来自中国天气网
成果
爬取所有城市的URL
一个城市对应一个URL,所以需要获得所有城市的URL并保存到本地文件。
运行该步骤的时间会很长,但是一劳永逸,只需要跑一遍。
也可以选择下载我提供的文件,我把该文件放到百度云了
链接: https://pan.baidu.com/s/1Mxok17-YhXP_OdjVHdyuYQ
密码: enpc
import requests
from bs4 import BeautifulSoup
from WebWorm.RandomHeader import getRandomHeader
import re
"""
1、参数:url
2、主要功能:根据url解析页面
3、返回值:html页面内容
"""
def parse(url):
# 设置随机头
headers = {"user-agent": getRandomHeader()}
# 请求URL
request = requests.get(url, headers=headers)
# 设置编码
request.encoding = 'utf-8'
# 请求到的内容
html = request.text
# 返回请求到的内容
return html
"""
1、参数:
nameAndUrl是存放地区名称和URL地址的数组
exceptionUrl是发生异常的URL
2、主要功能:获取直辖市的名称和对应的URL
3、返回值:nameAndUrl、exceptionUrl
"""
def zhiXiaShi(nameAndUrl, exceptionUrl):
# 直辖市 101是中国 后两位xx是直辖市的编码01-04 后四位xxxx是直辖市的区号 每次递增100
# 例如
# http://www.weather.com.cn/weather/101010200.shtml 北京海淀
# http://www.weather.com.cn/weather/101010300.shtml 北京朝阳
# url的前半部分
url = 'http://www.weather.com.cn/weather/101'
for i in range(1, 5):
# 直辖市编号
zhiXiaShi = str(i).zfill(2)
for k in range(100, 10000, 100):
# 直辖市区号
zhiXiaShiQu = str(k).zfill(4)
# 拼接URL
urlTemp = url + zhiXiaShi + zhiXiaShiQu + '.shtml'
# 如果解析过程中发生了错误异常,把这个URL加入数组,继续下次循环,因为有时网络波动会导致访问失败。
# 只要该URL发生了异常,就加入异常数组,等待后期处理,不会妨碍后面的URL继续执行。
try:
# 解析URL
html = parse(urlTemp)
# 如果URL是无效的
if html.__eq__('<!-- empty -->'):
# 退出循环
break
else:
# 获取该URL的名称
name = getName(html)
# 把地区名和URL同时加入数组,空格作为分隔符
nameAndUrl.append(name + ' ' + urlTemp)
print('获取' + name + '---成功')
except:
# 把URL加入异常数组
exceptionUrl.append(urlTemp)
# 返回数组
return nameAndUrl, exceptionUrl
"""
1、参数:
nameAndUrl是存放地区名称和URL地址的数组
exceptionUrl是发生异常的URL
2、主要功能:获取省的名称和对应的URL
3、返回值:nameAndUrl、exceptionUrl
"""
def sheng(nameAndUrl, exceptionUrl):
# 省 101是中国 后两位xx是省05-34 后两位xx是市 后两位xx是县 每次递增1
# 例如
# http://www.weather.com.cn/weather/101100203.shtml 山西大同大同县
# http://www.weather.com.cn/weather/101100204.shtml 山西大同天镇
# url的前半部分
url = 'http://www.weather.com.cn/weather/101'
for i in range(5, 35):
# 省编号
sheng = str(i).zfill(2)
for q in range(1, 100, 1):
# 市编号
shi = str(q).zfill(2)
# 如果解析过程中发生了错误异常,把这个URL记录下来,继续下次循环
try:
# 如果连01都没有,那么这个省就结束了
html = parse(url + sheng + shi + '01.shtml')
if html.__eq__('<!-- empty -->'):
# 退出循环
break
except:
# 把URL加入异常数组
exceptionUrl.append(url + sheng + shi + '01.shtml')
for f in range(1, 100, 1):
# 县编号
xian = str(f).zfill(2)
# 拼接URL
urlTemp = url + sheng + shi + xian + '.shtml'
# 如果解析过程中发生了错误异常,把这个URL记录下来,继续下次循环
try:
# 解析URL
html = parse(urlTemp)
# 如果URL是无效的
if html.__eq__('<!-- empty -->'):
# 退出循环
break
else:
# 获取该URL的名称
name = getName(html)
# 名称是全国开头的,才是我们需要的URL
if re.match('^全国.*', name):
# 把地区名和URL同时加入数组,空格作为分隔符
nameAndUrl.append(name + ' ' + urlTemp)
print('获取' + name + '---成功')
except:
# 把URL加入异常数组
exceptionUrl.append(urlTemp)
# 返回数组
return nameAndUrl, exceptionUrl
"""
1、参数:
nameAndUrl是存放地区名称和URL地址的数组
exceptionUrl是发生异常的URL
2、主要功能:
重新运行异常的URL
3、返回值:
nameAndUrl、exceptionUrl
"""
def solveExceptionUrl(nameAndUrl, exceptionUrl):
# 如果异常的URL列表不为空,继续循环
while len(exceptionUrl) != 0:
# 遍历异常数组
for i in exceptionUrl:
# 如果发生异常,执行下一个URL
try:
# 解析URL
html = parse(i)
# 如果URL是无效的
if html.__eq__('<!-- empty -->'):
# 移除URL
nameAndUrl.remove(i)
# 执行下一个URL
continue
else:
# 获取该URL的名称
name = getName(html)
# 名称是全国开头的,才是我们需要的URL
if re.match('^全国.*', name):
# 把地区名和URL同时加入数组,空格作为分隔符
nameAndUrl.append(name + ' ' + i)
print('获取' + name + '---成功')
# 移除URL
exceptionUrl.remove(i)
except:
# 执行下一个URL
continue
# 返回数组
return nameAndUrl
"""
1、参数:nameAndUrl是存放地区名称和URL地址的数组
主要功能:把数组中的数据写入文件
返回值:无
"""
def write(nameAndUrl):
# 把列表中信息写入文件
with open('nameAndUrl.txt', mode='w', encoding='utf-8') as file:
for i in nameAndUrl:
# 写入数据
file.write(i + '\n')
print('写入文件成功')
"""
1、参数:html内容
2、主要功能:
根据html获取城市的名称
3、返回值:
城市的名称
"""
def getName(html):
# 解析HTML
info = BeautifulSoup(html, "html.parser")
# 抓取class为crumbs fl的div
div = info.find(name="div", attrs={"class": "crumbs fl"})
# 从div中抓取a标签
a = div.findAll('a')
# 从div中抓取span标签
span = div.findAll('span')
# 城市名
name = ''
# 拼接城市名
for i in a:
name += i.text + '-'
name += span[-1].text
# 返回城市的名称
return name
if __name__ == '__main__':
# 存放URL的数组
nameAndUrl = []
# 存放发生异常的URL
exceptionUrl = []
# 爬取直辖市
nameAndUrl, exceptionUrl = zhiXiaShi(nameAndUrl, exceptionUrl)
# 爬取省
nameAndUrl, exceptionUrl = sheng(nameAndUrl, exceptionUrl)
# 重新解析异常的URL
nameAndUrl = solveExceptionUrl(nameAndUrl, exceptionUrl)
# 把数据写入文件
write(nameAndUrl)
根据城市URL爬取天气
代码结构图
import datetime
import requests
from bs4 import BeautifulSoup
from WebWorm.RandomHeader import getRandomHeader
"""
1、参数:
2、主要功能:
读取文件中的URL和Name
返回值:
包含URL和Name的数组
"""
def getURL():
with open('nameAndUrl.txt', mode='r', encoding='utf-8') as file:
# 读取文件
nameAndUrl = file.readlines()
# 返回数组
print('读取URL成功')
return nameAndUrl
"""
1、参数:URL
2、主要功能:
根据URL返回HTML内容
3、返回值:
HTML内容
"""
def parse(url):
# 设置随机头
headers = {"user-agent": getRandomHeader()}
# 请求URL
request = requests.get(url, headers=headers)
# 设置编码
request.encoding = 'utf-8'
# 请求到的内容
html = request.text
# 返回HTML
return html
"""
1、参数:html页面
2、主要功能:
爬取html页面的天气信息
3、返回值:
包含天气的数组
"""
def getWeatherInfo(html):
# 存放多组天气的数组
more = []
# 解析HTML
info = BeautifulSoup(html, "html.parser")
# 抓取class为crumbs fl的div
li = info.find(name="ul", attrs={"class": "t clearfix"}).find_all('li')
# 天数
num = 0
for i in li:
# 存放一个城市的天气信息
one = []
# 把城市名称加入数组
one.append(name)
# 不要当天的数据
if num == 0:
num += 1
continue
# 今天日期
today = datetime.datetime.now()
# 日期+1
offset = datetime.timedelta(days=+num)
# 时间并格式化
date = (today + offset).strftime('%Y-%m-%d')
# p class="wea" 天气
wea = i.find(name="p", attrs={"class": "wea"}).text
# <p class="tem"> 温度 如果是本天 只显示当前温度,所以n号晚上11点爬取,爬取n+1号的天气 <span>10℃</span>/<i>-1℃</i> <i>-3℃</i>
maxTem = i.find(name="p", attrs={"class": "tem"}).find('span').text.replace('℃', '')
minTem = i.find(name="p", attrs={"class": "tem"}).find('i').text.replace('℃', '')
# <p class="win"> 风
win = i.find(name="p", attrs={"class": "win"})
# 风况
winds = win.find('em').find_all('span')
wind = ''
for i in winds:
wind += i.get('title') + '转'
# 去除最后一个转
wind = wind[:-1]
# 风级
windNumber = win.find('i').text.replace('<', '小于')
wind += windNumber
# 日期+1
num += 1
# 把天气数据加入数组
one.append(date)
one.append(wea)
one.append(maxTem)
one.append(minTem)
one.append(wind)
# 把一组数据加入大数组
more.append(one)
print('爬取' + name + '成功')
return more
"""
1、参数:所有的天气信息
2、主要功能:
把爬取到的数据写入文件
3、返回值:
"""
def write(weather):
with open('weather.txt', mode='w', encoding='utf-8') as file:
for i in weather:
# |||作为分隔符
info = '|||'.join(i)
# 写入文件
file.write(info + '\n')
print('写入文件成功')
if __name__ == '__main__':
# 存放所有天气的信息的数组
allWeatherInfo = []
# 存放异常URL的数组
exceptionUrl = []
# 从文件中读取所有的URL
nameAndUrl = getURL()
# 遍历URL
for i in nameAndUrl:
# 城市名称
name = i.split(' ')[0]
# 城市URL
url = i.split(' ')[1].strip()
# 如果解析过程中发生了错误异常,把这个URL记录下来,继续下次循环
try:
# 获得HTML页面
html = parse(url)
# 获得天气信息
more = getWeatherInfo(html)
# 把天气信息加入数组
allWeatherInfo.extend(more)
except:
exceptionUrl.append(url)
# 解决异常URL,知道异常URL数组为空
while len(exceptionUrl) != 0:
# 遍历异常URL
for i in exceptionUrl:
# 如果URL发生异常,继续循环
try:
# 获得HTML页面
html = parse(i)
# 得到天气信息
more = getWeatherInfo(html)
# 把天气信息加入数组
allWeatherInfo.extend(more)
# 爬取成功,移除异常URL
exceptionUrl.remove(i)
except:
continue
# 把天气信息写入文件
write(allWeatherInfo)
总结
可以把天气信息插入数据库,也可以每天定时执行脚本,我提供了面粉和水,至于你们想做饺子还是包子,取决于个人。
有问题及时反馈,共同成长。