基于BeautifulSoup解析的网页爬虫实现

目标

爬去cnkongqi.com上的气象数据,cnkongqi.com站点上包含全国每个城市的天气,空气质量等数据,这些数据每小时都会进行更新。我的目标是要将该站点的某一时间的气象数据全部抓取下来,并保存到数据库。

本次爬虫编写语言选用python,由于在下对python是小白,所以这个爬虫程序可以算是我的第一个完整的python爬虫。以前使用过java编写一些爬虫程序,爬去够某些小说网站的小说,所以对爬虫的原理有着一定的理解,根据该原理使用python进行实现。

分析

cnkongqi.com上的某一个城市的气象数据地址为:http://www.cnkongqi.com/pc/510100.htm ,每小时数据更新后地址不变。而且每个页面都包含一个城市目录,该站点没有单独的目录页,所以需要预先在本地创建城市数据页面目录。创建好城市目录后,即可进行遍历目录进行数据爬去和存储。如果需要不断的更新本地数据,只需要每小时执行一次便利代码即可。需要完成:

  • 目录创建
  • 遍历目录获取气象信息你

实现

  • 使用urllib来获取目标页面html
  • 使用Beautifulsoup解析html
  • 使用pymysql进行数据存储

数据库设计

根据站点数据结构特点,设计了4张表:
- 省份目录 t_province

CREATE TABLE t_province
(
    id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
    name VARCHAR(20)
);
  • 城市目录 t_city
CREATE TABLE t_city
(
    id INT(11) PRIMARY KEY NOT NULL,
    name VARCHAR(30),
    provinceId INT(11),
    infoUrl VARCHAR(50),
    updateTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
  • 城市气象数据 t_city_data
CREATE TABLE t_city_data
(
    id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
    tq VARCHAR(5) COMMENT '天气',
    wd FLOAT(5,2) COMMENT '温度 。C',
    sd FLOAT(5,2) COMMENT '湿度 %',
    fx VARCHAR(20) COMMENT '风向',
    zs INT(11) COMMENT '指数',
    dj VARCHAR(20) COMMENT '等级',
    cityId INT(11) COMMENT '城市编号',
    updateTime TIMESTAMP COMMENT '更新时间'
);
  • 城市观测点空气质量数据 t_city_point_data
CREATE TABLE t_city_point_data
(
    id INT(11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
    pointName VARCHAR(20) COMMENT '监测点名称',
    pm25 FLOAT(6,2) COMMENT 'pm2.5浓度',
    aqi_cn INT(11) COMMENT '中标AQI',
    aqi_us INT(11) COMMENT '美标AQI',
    cityId INT(11) COMMENT '城市编号',
    updateTime TIMESTAMP COMMENT '更新时间'
);

省份及城市目录创建

目录创建代码仅需执行一次即可。

由于python的易读性,就不赘述了,直接上代码:

from urllib.request import urlopen
from bs4 import BeautifulSoup
import pymysql

# 创建数据库连接
conn = pymysql.connect(host='127.0.0.1', user='root', passwd='123456', db='test', charset="utf8")
cur = conn.cursor()
cur.execute("use test")

# 存储省份数据
# code - 省份编号
# name - 省份中文名
def storeProvince(code, name):
    cur.execute("INSERT INTO t_province VALUES (%s,%s)", (code, name))
    cur.connection.commit()

# 存储城市目录
# code - 城市编号
# name - 城市中文名
# pcode - 所属省份编号
# url - 城市气象数据地址
def storeCity(code, name, pcode, url):
    cur.execute("INSERT INTO t_city (id, `name`, provinceId, infoUrl) VALUES (%s,%s,%s,%s)", (code, name, pcode, url))
    cur.connection.commit()

# 任意城市页面html获取
html = urlopen("http://www.cnkongqi.com/pc/510100.htm")

# 解析该页面的html
# 注:不添加"html.parse" 会报错,但是参考代码上却没有该参数,对此比较纳闷,求高手指教
bsObj = BeautifulSoup(html.read(), "html.parser")

# 获取该页面的目录信息,并存储
for link in bsObj.find("div", {"id": "div2"}).findAll("a"):
    # 获取省份信息
    code = link.attrs['id'][4:]

    # 存储
    storeProvince(link.attrs['id'][4:], link.get_text())

    # 获取该省份的城市信息
    for alink in bsObj.find("div", {"id": "city_div_" + code}).findAll("a"):
        ccode = alink.attrs['id'][5:]
        cname = alink.get_text()
        curl = "http://www.cnkongqi.com/pc/" + alink.attrs['href']
        # 存储
        storeCity(ccode, cname, code, curl)

# 关闭资源
cur.close()
conn.close()

遍历目录进行气象数据抓取

数据抓取代码可以重复使用,该站点并未对采集频率进行限制。

from urllib.request import urlopen
from bs4 import BeautifulSoup
import pymysql
from pymysql import DataError

# 创建数据库连接
conn = pymysql.connect(host='127.0.0.1',user='root',passwd='123456',db='test',charset = "utf8")
cur = conn.cursor()
cur.execute("use test")
updatecur = conn.cursor()
updatecur.execute("use test")

# 存储城市气象数据
# data - 一条城市数据元组
def saveCityData(data):
    try:
        updatecur.execute("INSERT INTO t_city_data (tq,wd,sd,fx,zs,dj,cityId,updateTime) VALUES (%s,%s,%s,%s,%s,%s,%s,now())", data)
        updatecur.connection.commit()
    except DataError:
        print(data)

# 存储观测点数据
# data - 一条观测点数据元组
def saveCityPointData(data):
    try:
        updatecur.execute("INSERT INTO t_city_point_data (pointName, pm25, aqi_cn, aqi_us, cityId,updateTime) VALUES (%s,%s,%s,%s,%s,now())", data)
        updatecur.connection.commit()
    except DataError:
        print(data)

# 更新一个城市的更新时间
def updateCity(code):
    updatecur.execute("UPDATE t_city SET updateTime=now() WHERE id=%s", (code))
    updatecur.connection.commit()

# 获取html中某个节点的数据,并设置默认值
def getData(source,index, default):
    try:
        return source[index].get_text()
    except IndexError:
        return default

# 空值替换
def replaceEmpty(data):
    if(data == ''):
        return '0'
    else:
        return data

# 城市目录获取
cur.execute("SELECT * FROM t_city")

# 遍历目录获取城市气象信息
for var in cur.fetchall():
    cityCode = var[0]
    url = var[3]

    # 城市气象信息页面html获取
    html = urlopen(url)
    # html 解析
    bsObj = BeautifulSoup(html.read(), "html.parser")

    # 城市气象信息解析
    left_left = bsObj.find("div",{"class":"s_left-left"}).findAll("li")
    left_right = bsObj.find("div",{"class":"s_right-right"}).findAll("span")
    tq = getData(left_left,0,'')
    wd = getData(left_left,1,'温度-273.15。C')
    sd = getData(left_left,2,'湿度0%')
    fx = getData(left_left,3,'-')
    zs = getData(left_right,0,'0')
    dj = getData(left_right,1,'-')
    # 封装元组
    cityData = (tq[2:],replaceEmpty(wd[2:len(wd)-2]),replaceEmpty(sd[2:len(sd)-1]),fx,zs.replace('-','0'),dj,cityCode)
    # 存储
    saveCityData(cityData)

    # 获取观测点列表
    pointTableTr = bsObj.findAll("table",{"class":"tbaqi"})[1].findAll("tr")
    # 遍历观测点
    for tr in pointTableTr:
        # 空气质量数据解析,并封装元组
        td = tr.findAll("td")
        pointData = (td[0].get_text(),td[1].get_text().replace('-','0'),td[2].get_text().replace('-','0'),td[3].get_text().replace('-','0'),cityCode)
        # 存储
        saveCityPointData(pointData)
    # 更新该城市,设置更新时间为当前时间
    updateCity(cityCode)

# 资源关闭
updatecur.close()
cur.close()
conn.close()
posted @ 2016-12-28 22:04  吴昭  阅读(193)  评论(0编辑  收藏  举报