全国历史天气查询/历史天气预报查询——全国各月份数据爬取

全国历史天气查询/历史天气预报查询——全国各月份数据爬取

效果

图1 目标爬取数据
图2 最终实验效果

实验效果:最终可将官网已有的数据进行爬取整理,共363个城市,从2011年1月——至今

数据已上传至CSDN,若无C币的小伙伴可以自行爬取或至QQ群:782589269,群文件中免费下载使用



全国历史天气查询_历史天气预报查询_温度查询_天气后报官网,戳它直达!

导入所需库

import os
import re
import time
import pandas as pd
from tqdm import tqdm
import pickle
import requests

分析官网HTML内容

首先,进行官网HTML内容的分析,找出我们所需信息的代码段
官网链接:http://www.tianqihoubao.com/lishi/

url = "http://www.tianqihoubao.com/lishi/"
url_request = requests.get(url)
url_request.encoding = 'gb2312'
url_text = url_request.text
print(url_text)

在这里插入图片描述
可以看出我们要爬取的城市名在,诸如这样的<a>城市名</a>代码块

<a href="/lishi/bj.htm" title=“北京历史天气预报”><b>北京</b></a>

使用正则抽取所需的城市名信息

若不懂正则表达式,在此,你只需了解在正则表达式中可以用(\w+)来提取所需内容即可
比如对于上例中的链接,将其中的‘北京’换成(\w+)即可,对于各城市相同的部分保持不变,各城市不同的部分使用正则表达式替换,不加括号代表不提取,即\w+
因此,可以得到下述pattern提取公式

url ='<a href="/lishi/beijing.html" title="北京历史天气查询">北京 </a>'
pattern = '<a href="/lishi/(\w+.html)" title="\w+">(\w+) </a>'

实验测试

exapmle_text = '<a href="/lishi/beijing.html" title="北京历史天气查询">北京 </a>'
pattern =  re.compile(r'<a href="/lishi/(\w+.html)" title="\w+">(\w+) </a>') 
example_city = pattern.findall(exapmle_text)
print(example_city)

在这里插入图片描述

爬取全部的城市名与链接

pattern =  re.compile(r'<a href="/lishi/(\w+.html)" title="\w+">(\w+) </a>') 
citys = pattern.findall(url_text) # 获取363个城市名
print("已获取{}个城市名,第一个城市是{},最后一个城市是{}".format(len(citys),citys[0],citys[-1]))

在这里插入图片描述
进行城市各月份链接的爬取,以同样的方法分析其中一个城市的各月份链接地址,如北京
北京2011年1月的链接为
http://www.tianqihoubao.com/lishi/beijing/month/201101.html
北京2011年2月的链接为
http://www.tianqihoubao.com/lishi/beijing/month/201102.html

可以得到如下规律

  • 基地址:http://www.tianqihoubao.com/lishi/
  • 城市地址:beijing/
  • 月份地址:month/201101.html
  • 各个城市的月份链接地址=基地址+城市地址+月份地址

以此规律进行爬取即可?
不可,在笔者尝试N次后,发现必然部分城市数据爬取不全,为什么呢?因为这个网站的HTML语言不是均按照上述规律写的(可能官网的程序员中途换人了,真的太让人头秃了)

实验发现,大约对于同一个城市有大约这样四种神奇的HTML语言格式,在此就是一展你正则表达式的水平的时候了…(写了个爬虫,正则倒是进步了)

exapmle_text = '''
    <li><a href='/lishi/beijing/month/201101.html' title=\"北京2011年01月天气\">2011年1月北京天气</a></li>
    <li><a href='/lishi/beijing/month/201604.html' >2016年4月北京天气</a></li>
    <li><a href='/lishi/beijing/month/201605.html'>2016年5月北京天气</a></li>
    <li><a href='beijing/month/201612.html'>2016年12月北京天气</a></li>
'''

pattern =  re.compile(r"<li><a href='/lishi/(\w+/\w+/\w+.html)'\s?title=\"\w+\">\w+</a></li>|<li><a href='(?:/lishi/)?(\w+/\w+/\w+.html)'\s?>\w+</a></li>") 
pattern.findall(exapmle_text)

在这里插入图片描述

进行城市月份数据的爬取

city_urls = {}
base_url = 'http://www.tianqihoubao.com/lishi/'

for url_city in citys:
    url,city = url_city
    city_base_url = base_url + url
    city_urls[city] = []
    
    pattern =  re.compile(r"<li><a href='/lishi/(\w+/\w+/\w+.html)'\s?title=\"\w+\">\w+</a></li>|<li><a href='(?:/lishi/)?(\w+/\w+/\w+.html)'\s?>\w+</a></li>") 
    city_url_request = requests.get(city_base_url)
    for tuple_ in pattern.findall(city_url_request.text):
        i,j = tuple_
        if i.strip():
            i = base_url + i
            city_urls[city].append(i)
        else:
            j = base_url + j
            city_urls[city].append(j)
    
    
    print('{}:{}份数据'.format(city,len(city_urls[city])))
    print('示例链接:',city_urls[city][0],'\n')

保存城市名与基链接

本地保存后,即使后面实验失败了,也可以从此再次断点重启
通过pickle保存字典

with open("city_urls.file", "wb") as f:
    pickle.dump(city_urls, f)
# 通过pickle读取字典
with open("city_urls.file", "rb") as f:
    city_urls = pickle.load(f)

各城市的月份数据爬取

df:将保存所有数据,以城市列为区分
df_city:将保存各个城市单独的数据
error:不断记录爬取失败的城市与对应链接

df = pd.DataFrame(columns = ['日期', '天气状况', '气温', '风力风向','城市'])
error = []

for city in tqdm(city_urls.keys()):
    df_city = pd.DataFrame(columns = ['日期', '天气状况', '气温', '风力风向','城市'])
    
    if city + '.csv' in os.listdir('./output/'):
        continue
    for month_url in city_urls[city]:
        try:
            dataframe = pd.read_html(month_url,encoding = 'gb2312')[0]
            if dataframe.shape[0] == 0:
                dataframe = pd.DataFrame(columns = ['日期', '天气状况', '气温', '风力风向','城市'])
                dataframe.loc[0,:] = [None,None,None,None,city]
            else:
                dataframe = dataframe.loc[1:,:]
                dataframe.columns = ['日期', '天气状况', '气温', '风力风向']
                dataframe['城市'] = city
            df_city = df_city.append(dataframe)
            df = df.append(dataframe)
        except:
            print('Error')
            print(month_url)
            error.append((city,month_url))
            continue
            
    df_city.to_csv('./output/'+city+'.csv',index=False)   
    print('城市:\t',city,'数据收集完毕')
print('所有城市数据,已收集完成!')   

在这里插入图片描述

  • 7
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 13
    评论
在上一篇文章中,我们介绍了如何通过Java代码模拟浏览器行为来爬取携程网站上的酒店列表数据。本篇文章将继续介绍如何通过Java代码获取酒店详情页面的数据。 1. 获取酒店详情页面URL 在上一篇文章中,我们已经可以获取到酒店列表页面的数据。接下来,我们需要从列表页面中获取每个酒店的详情页面URL。我们可以通过以下代码来获取URL: ```java // 获取酒店详情页面URL Elements hotelLinks = doc.select(".hotel_item a[href^=//hotels.ctrip.com/hotel/]"); for (Element link : hotelLinks) { String href = link.attr("href"); hotelUrls.add("http:" + href); } ``` 这里我们使用了Jsoup框架提供的选择器功能,选取class为“hotel_item”的元素,然后再选取其中的a标签,并且href属性值以“//hotels.ctrip.com/hotel/”开头的元素。然后再遍历这些元素,获取其中的href属性值并加上“http:”前缀,即是酒店详情页面的URL。 2. 解析酒店详情页面数据 获取到酒店详情页面的URL后,我们需要再次模拟浏览器行为,访问该URL并获取页面数据。我们可以通过以下代码来实现: ```java // 获取酒店详情页面数据 for (String url : hotelUrls) { Document hotelDoc = Jsoup.connect(url).userAgent(USER_AGENT).get(); String hotelName = hotelDoc.select(".hotel_intro dt").text(); String address = hotelDoc.select(".hotel_address a").text(); String score = hotelDoc.select(".hotel_score .score_num").text(); String commentNum = hotelDoc.select(".hotel_judgement .hotel_judgement_judgement").text(); String price = hotelDoc.select(".hotel_intro .hotel_price span").text(); hotels.add(new Hotel(hotelName, address, score, commentNum, price)); } ``` 这里我们同样使用了Jsoup框架来获取页面数据。首先,我们遍历所有酒店详情页面的URL,然后通过Jsoup.connect()方法连接到该URL并获取页面数据。接着,我们使用选择器选取需要的元素,例如酒店名称、地址、评分、评论数和价格等,并将其保存到一个自定义的Hotel对象中。最后,我们将所有的Hotel对象添加到一个列表中,以便后续的数据处理。 3. 数据保存 获取到所有酒店的数据后,我们可以将其保存到本地文件中,也可以将其保存到数据库中。这里我们使用了JDBC连接MySQL数据库,并将数据保存到一个名为“hotels”的表中。以下是保存数据的代码: ```java // 保存酒店数据数据库 Connection conn = null; PreparedStatement stmt = null; try { Class.forName(JDBC_DRIVER); conn = DriverManager.getConnection(DB_URL, USER, PASS); String sql = "INSERT INTO hotels (name, address, score, comment_num, price) VALUES (?, ?, ?, ?, ?)"; stmt = conn.prepareStatement(sql); for (Hotel hotel : hotels) { stmt.setString(1, hotel.getName()); stmt.setString(2, hotel.getAddress()); stmt.setString(3, hotel.getScore()); stmt.setString(4, hotel.getCommentNum()); stmt.setString(5, hotel.getPrice()); stmt.executeUpdate(); } } catch (SQLException se) { se.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { try { if (stmt != null) stmt.close(); } catch (SQLException se2) { } try { if (conn != null) conn.close(); } catch (SQLException se) { se.printStackTrace(); } } ``` 这里我们使用了JDBC框架来连接数据库,并执行SQL语句将酒店数据保存到数据库中。最后,我们需要在finally块中关闭数据库连接和语句对象,以释放资源。 总结 本篇文章介绍了如何通过Java代码获取携程网站上的酒店详情页面数据,并将其保存到MySQL数据库中。这个过程涉及到了许多技术,例如模拟浏览器行为、页面数据解析、数据库连接等。通过这个案例,我们可以更好地理解Java爬虫的实现原理和技术要点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿芒Aris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值