先给大家看一看效果图(我一共获取到了462个城市的天气):
前不久,2019年开放数据中心峰会在北京国际会议中心成功召开,ODCC指出:“对数据进行汇聚,在体系化融合中产生新的价值已成为未来发展的关键”
在DT时代,谁拥有数据,谁就有筹码。话不多说,我们先来了解一下,什么是大数据?简单来说,假如你是一名气象员,负责统计某个城市的天气情况(最高温度、最低温度、风速等等),如果你每小时统计一次,那么,一天下来,你可以获得24条数据,如果你雇了很多人一起统计你所在城市的每个城区的天气,假设该城市有5个城区,那么,你在一天之内就能收集245=120条数据,再把范围扩大,我们把全国各个城市的天气数据汇聚在一起,(我查了一下,截止2018年,全国共有661个市),那么你在一天之内,就收集到了120661=79320条数据!这仅仅只是一天的数据,如果你坚持一周、一个月甚至更久…
那么,我们现在就开始收集数据吧!(我虽然不是气象员,但是我可以通过程序获取天气数据,数据来源:中国天气网)中国天气网没有运用ajax等加载技术,这样比较方便一个爬虫新手对其进行爬取,在爬取过程中只需要对一些文本进行格式化就行,网上有很多爬中国天气网的教程,这里就不详细说了,我只说一下我改进的地方:
- 每次运行程序时可以自动创建数据表,把抓取下来的数据存进MySQL数据库中,按照时间建立数据表,方便日后做数据分析
- 网上的教程没有讲港澳台地区的数据要怎么爬(说是因为表格结构不同),我为了数据的完整性,把抓取港澳台地区数据的问题解决了
- 对比BeautifulSoup 和 Xpath 这两种方法.在爬港澳台地区数据时,我用了xpath
在这里,我将用到面向对象编程.首先使用 class 语句来创建一个新类,class 之后为类的名称并以冒号结尾:
class Inquire(object):
init()方法是一种特殊的方法,被称为类的构造函数或初始化方法,当创建了这个类的实例时就会调用该方法:
def __init__(self):
self 代表类的实例,self 在定义类的方法时是必须有的,在调用时不必传入相应的参数
接下来,我们来连接一下数据库:
self.conn = connect(host = '127.0.0.1',
port = 3306,
user = 'root',
password = 'root',
charset = 'utf8',
db = 'asset')
self.cursor = self.conn.cursor()
print("数据库连接成功!")
输出一个结果用来表示连接成功,这里是程序的最开始部分,更优的写法应该是这样的:
try:
self.conn = connect(host = '127.0.0.1',
port = 3306,
user = 'root',
password = 'root',
charset = 'utf8',
db = 'asset')
self.cursor = self.conn.cursor()
print("数据库连接成功!")
except Exception as e:
print(e)
接下来,我来讲一下我的想法:
获取当前的时间,以当前时间给数据表命名,为了防止一天要运行多次程序,我在后面添上了”a+数字”,20190928a1则表示2019年9月28日的第一张table, 20190928a2则表示2019年9月28日的第二张table,以此类推… …
def ticks_time(self):
ticks = time.strftime("%Y%m%da1", time.localtime())
print("当前时间为:", ticks)
return ticks
有了给数据表建表的名称,我们开始创建数据表:
def creat_table(self):
ticks = self.ticks_time()
self.cursor.execute("CREATE TABLE `%s` (id INT AUTO_INCREMENT PRIMARY KEY,city CHAR(10),weather CHAR(10),wind CHAR(20),max CHAR(10),min CHAR(10))"%ticks)
print("数据表创建成功!")
我们来看一下数据表创建成功后,在MySQL里显示的结果:
接下来就到数据抓取的部分了,我主要讲如何爬港澳台地区的数据,先来看一下港澳台地区的网页结构:
根据这个结构,我们可以分析:
- 首先要定位到hanml, 因为下面有很多个comMidtab
- 通过hanml定位到comMidtab
- 取出第一个conMidtab
- 定位到下面的table标签
- 循环该节点下的table
- 通过每个table定位到第三个tr
- 从第三个tr开始,取出tr下的所有td,并且找到相应的文字内容
还是挺复杂的,但是没关系,分步骤来进行,就会变得很简单,开始写代码!
url = "http://www.weather.com.cn/textFC/gat.shtml"
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"}
html = requests.get(url=url,headers=headers)
selector = etree.HTML(html.content,parser=etree.HTMLParser(encoding='utf8'))
前几句都是常规操作了,我讲讲最后一句,我一开始就是在这里遇到了不少问题.
我直接使用lxml的etree.HTML处理源代码,然后Xpath提取内容时,出现了乱码,我上网查了一下,找到了原因:处理源文件的时候,由于没有指定编码,所以它使用了一个默认编码,从而导致和UTF-8冲突,产生乱码。经过查阅lxml.etree.HTML的文档,我发现etree.HTML有一个参数是parser,这个参数不是必须的,因此省略以后它就会自动使用一个默认的parser。
既然如此,那我手动指定一个:
etree.HTML(html.content, parser=etree.HTMLParser(encoding='utf8'))
这里我指定了etree.HTMLParser来作为一个parser,同时,etree.HTMLParser可以接受编码作为参数。于是我指定为UTF-8.
到这一步以后,我们开始定位元素.
定位hanml:
hanml = selector.xpath("//div[@class='hanml']")[0]
定位hanml下的conMidtab:
conMidtab = hanml.xpath("./div[@class='conMidtab']")[0]
定位conMidtab下的table标签:
tables = conMidtab.xpath("./div[@class='conMidtab2']//table")[0:3]
循环该节点下的table, 过每个table定位到第三个tr:
for table in tables:
tr_list = table.xpath("./tr")[2:]
从第三个tr开始,取出tr下的所有td,并且找到相应的文字内容:
for index,tr_list in enumerate(tr_list):
td_list = tr_list.xpath("./td//text()")
我们来打印一下:
我拿出来给大家看:
[’\n’, ‘香港’, ‘\n’, ‘\n’, ‘香港’, ‘晴’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘32’, ‘晴’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘26’, ‘\n’, ‘详情’]
[’\n’, ‘澳门’, ‘\n’, ‘\n’, ‘澳门’, ‘多云’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘32’, ‘多云’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘25’, ‘\n’, ‘详情’]
[’\n’, ‘台湾’, ‘\n’, ‘\n’, ‘台北’, ‘小雨’, ‘\n’, ‘东北风’, ‘\n’, ‘3-4级’, ‘26’, ‘小雨’, ‘\n’, ‘东风’, ‘\n’, ‘3-4级’, ‘23’, ‘\n’, ‘详情’]
[’\n’, ‘高雄’, ‘晴’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘28’, ‘晴’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘25’, ‘\n’, ‘详情’]
[’\n’, ‘台中’, ‘小雨’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘26’, ‘晴’, ‘\n’, ‘无持续风向’, ‘\n’, ‘<3级’, ‘21’, ‘\n’, ‘详情’]
可以看到,前三行和后两行的长度是不一样的,我第一次就踩了这个坑,因此,我们要做一个判断.在这里,我用列表长度做的判断:
len_td = len(td_list)
if len_td == 19:
city_name = td_list[4]
weather = td_list[11]
wind = td_list[13]
max = td_list[10]
min = td_list[16]
else:
city_name = td_list[1]
weather = td_list[8]
wind = td_list[10]
max = td_list[13]
min = td_list[7]
否则在匹配时会出现错位的情况,再打印一下:
print(city_name, weather, wind, max, min)
结果是:
香港 晴 无持续风向 32 26
澳门 多云 无持续风向 32 25
台北 小雨 东风 26 23
高雄 晴 无持续风向 25 28
台中 晴 无持续风向 21 26
完美!我们开始导入数据库:
sql = 'insert into %s(city,weather,wind,max,min)'%ticks + 'value(%s,%s,%s,%s,%s)'
self.cursor.execute(sql,[city_name,weather,wind,max,min])
self.conn.commit()
到这里就结束了!我们来看看数据库表的表数据:
我省略了部分代码,在这里我只展示代码的缩略图.,如有需要,可以联系我:
QQ:2733821739 哓哓晓培