zoukankan      html  css  js  c++  java
  • Scrapy实战篇(五)之爬取历史天气数据

      本篇文章我们以抓取历史天气数据为例,简单说明数据抓取的两种方式:

      1、一般简单或者较小量的数据需求,我们以requests(selenum)+beautiful的方式抓取数据

      2、当我们需要的数据量较多时,建议采用scrapy框架进行数据采集,scrapy框架采用异步方式发起请求,数据抓取效率极高。

      下面我们以http://www.tianqihoubao.com/lishi/网站数据抓取为例进行进行两种数据抓取得介绍:  

      1、以request+bs的方式采集天气数据,并以mysql存储数据

      思路:

      我们要采集的天气数据都在地址 http://www.tianqihoubao.com/lishi/beijing/month/201101.html 中存储,观察url可以发现,url中只有两部分在变化,即城市名称和你年月,而且每年都固定包含12个月份,可以使用 months = list(range(1, 13))构造月份,将城市名称和年份作为变量即可构造出需要采集数据的url列表,遍历列表,请求url,解析response,即可获取数据。

      

      以上是我们采集天气数据的思路,首先我们需要构造url链接。

      

     1 def get_url(cityname,start_year,end_year):
     2     years = list(range(start_year, end_year))
     3     months = list(range(1, 13))
     4     suburl = 'http://www.tianqihoubao.com/lishi/'
     5     urllist = []
     6     for year in years:
     7         for month in months:
     8             if month < 10:
     9                 url = suburl + cityname + '/month/'+ str(year) + (str(0) + str(month)) + '.html'
    10             else:
    11                 url = suburl + cityname + '/month/' + str(year) + str(month) + '.html'
    12             urllist.append(url.strip())
    13     return urllist

          通过以上函数,可以得到需要抓取的url列表。  

      可以看到,我们在上面使用了cityname,而cityname就是我们需要抓取的城市的城市名称,需要我们手工构造,假设我们已经构造了城市名称的列表,存储在mysql数据库中,我们需要查询数据库获取城市名称,遍历它,将城市名称和开始年份,结束年份,给上面的函数。

    1 def get_cityid(db_conn,db_cur,url):
    2     suburl = url.split('/')
    3     sql = 'select cityid from city where cityname = %s '
    4     db_cur.execute(sql,suburl[4])
    5     cityid = db_cur.fetchone()
    6     idlist = list(cityid)
    7     return idlist[0]

      有了城市代码,开始和结束年份,生成了url列表,接下来当然就是请求地址,解析返回的html代码了,此处我们以bs解析网页源代码,代码如下:

     1 def parse_html_bs(db_conn,db_cur,url):
     2     proxy = get_proxy()
     3     proxies = {
     4         'http': 'http://' + proxy,
     5         'https': 'https://' + proxy,
     6     }
     7     headers = {
     8         'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',
     9         'Connection': 'close'
    10     }
    11 
    12     # 获取天气数据的html网页源代码
    13     weather_data = requests.get(url=url, headers=headers,proxies = proxies).text
    14     weather_data_new =(weather_data.replace('
    ','').replace('
    ','').replace(' ',''))
    15     soup = BeautifulSoup(weather_data_new,'lxml')
    16     table = soup.find_all(['td'])
    17     # 获取城市id
    18     cityid = get_cityid(db_conn, db_cur, url)
    19     listall = []
    20     for t in list(table):
    21         ts = t.string
    22         listall.append(ts)
    23     n= 4
    24     sublist = [listall[i:i+n] for i in range(0,len(listall),n)]
    25     sublist.remove(sublist[0])
    26     flist = []
    27     # 将列表元素中的最高和最低气温拆分,方便后续数据分析,并插入城市代码
    28     for sub in sublist:
    29         if sub == sublist[0]:
    30             pass
    31         sub2 = sub[2].split('/')
    32         sub.remove(sub[2])
    33         sub.insert(2, sub2[0])
    34         sub.insert(3, sub2[1])
    35         sub.insert(0, cityid)  # 插入城市代码
    36         flist.append(sub)
    37     return flist

      最后我们在主函数中遍历上面的列表,并将解析出来的结果存储到mysql数据库。

     1 if __name__ == '__main__':
     2     citylist = get_cityname(db_conn,db_cur)
     3     for city in citylist:
     4         urllist = get_url(city,2016,2019)
     5         for url in urllist:
     6             time.sleep(1)
     7             flist = parse_html_bs(db_conn, db_cur, url)
     8             for li in flist:
     9                 tool.dyn_insert_sql('weather',tuple(li),db_conn,db_cur)
    10                 time.sleep(1)

    以上我们便完成了以requests+bs方式抓取历史天气数据,并以mysql存储的程序代码,完成代码见:https://gitee.com/liangxinbin/Scrpay/blob/master/weatherData.py

      2、用scrapy框架采集天气数据,并以mongo存储数据

      1)定义我们需要抓取的数据结构,修改框架中的items.py文件

    1 class WeatherItem(scrapy.Item):
    2     # define the fields for your item here like:
    3     # name = scrapy.Field()
    4     cityname = Field()   #城市名称
    5     data = Field()    #日期
    6     tq = Field()     #天气
    7     maxtemp=Field()    #最高温度
    8     mintemp=Field()   #最低温度
    9     fengli=Field()    #风力

      2)修改下载器中间件,随机获取user-agent,ip地址

     1 class RandomUserAgentMiddleware():
     2     def __init__(self,UA):
     3         self.user_agents = UA
     4 
     5     @classmethod
     6     def from_crawler(cls, crawler):
     7         return cls(UA = crawler.settings.get('MY_USER_AGENT'))   #MY_USER_AGENT在settings文件中配置,通过类方法获取
     8 
     9     def process_request(self,request,spider):
    10         request.headers['User-Agent'] = random.choice(self.user_agents)    #随机获取USER_AGENT
    11 
    12     def process_response(self,request, response, spider):
    13         return response
    14 
    15 
    16 class ProxyMiddleware():
    17     def __init__(self):
    18         ipproxy = requests.get('http://localhost:5000/random/')   #此地址为从代理池中随机获取可用代理
    19         self.random_ip = 'http://' + ipproxy.text
    20 
    21     def process_request(self,request,spider):
    22         print(self.random_ip)
    23         request.meta['proxy'] = self.random_ip
    24 
    25     def process_response(self,request, response, spider):
    26         return response

      3)修改pipeline文件,处理返回的item,处理蜘蛛文件返回的item

      

     1 import pymongo
     2 
     3 class MongoPipeline(object):
     4 
     5     def __init__(self,mongo_url,mongo_db,collection):
     6         self.mongo_url = mongo_url
     7         self.mongo_db = mongo_db
     8         self.collection = collection
     9 
    10     @classmethod
    14     def from_crawler(cls,crawler):
    15         return cls(
    16             mongo_url=crawler.settings.get('MONGO_URL'),    #MONGO_URL,MONGO_DB,COLLECTION在settings文件中配置,通过类方法获取数据
    17             mongo_db = crawler.settings.get('MONGO_DB'),
    18             collection = crawler.settings.get('COLLECTION')
    19         )
    20 
    21     def open_spider(self,spider):
    22         self.client = pymongo.MongoClient(self.mongo_url)
    23         self.db = self.client[self.mongo_db]
    24 
    25     def process_item(self,item, spider):
    26         # name = item.__class__.collection
    27         name = self.collection
    28         self.db[name].insert(dict(item)) #将数据插入到mongodb数据库。
    29         return item
    30 
    31     def close_spider(self,spider):
    32         self.client.close()

      4)最后也是最重要的,编写蜘蛛文件解析数据,先上代码,在解释

     1 # -*- coding: utf-8 -*-
     2 import scrapy
     3 from bs4 import BeautifulSoup
     4 from scrapy import Request
     5 from lxml import etree
     6 from scrapymodel.items import WeatherItem
     7 
     8 
     9 class WeatherSpider(scrapy.Spider):
    10     name = 'weather'        #蜘蛛的名称,在整个项目中必须唯一
    11     # allowed_domains = ['tianqihoubao']
    12     start_urls = ['http://www.tianqihoubao.com/lishi/']    #起始链接,用这个链接作为开始,爬取数据,它的返回数据默认返回给parse来解析13  
    14 
    15     #解析http://www.tianqihoubao.com/lishi/网页,提取连接形式http://www.tianqihoubao.com/lishi/beijing.html
    16     def parse(self, response):
    17         soup = BeautifulSoup(response.text, 'lxml')
    18         citylists = soup.find_all(name='div', class_='citychk')
    19         for citys in citylists:
    20             for city in citys.find_all(name='dd'):
    21                 url = 'http://www.tianqihoubao.com' + city.a['href']
    22                 yield Request(url=url,callback = self.parse_citylist)    #返回Request对象,作为新的url由框架进行调度请求,返回的response有回调函数parse_citylist进行解析
    23 
    24     #解析http://www.tianqihoubao.com/lishi/beijing.html网页,提取链接形式为http://www.tianqihoubao.com/lishi/tianjin/month/201811.html
    25     def parse_citylist(self,response):
    26         soup = BeautifulSoup(response.text, 'lxml')
    27         monthlist = soup.find_all(name='div', class_='wdetail')
    28         for months in monthlist:
    29             for month in months.find_all(name='li'):
    30                 if month.text.endswith("季度:"):
    31                     continue
    32                 else:
    33                     url = month.a['href']
    34                     url = 'http://www.tianqihoubao.com' + url
    35                     yield Request(url= url,callback = self.parse_weather) #返回Request对象,作为新的url由框架进行调度请求,返回的response由parse_weather进行解析
    36 
    37     # 以xpath解析网页数据;
    38     def parse_weather(self,response):    #解析网页数据,返回数据给pipeline处理
    39         # 获取城市名称
    40         url = response.url
    41         cityname = url.split('/')[4]
    42 
    43         weather_html = etree.HTML(response.text)
    44         table = weather_html.xpath('//table//tr//td//text()')
    45         # 获取所有日期相关的数据,存储在列表中
    46         listall = []
    47         for t in table:
    48             if t.strip() == '':
    49                 continue
    50             # 替换元素中的空格和
    
    51             t1 = t.replace(' ', '')
    52             t2 = t1.replace('
    ', '')
    53             listall.append(t2.strip())
    54         # 对提取到的列表数据进行拆分,将一个月的天气数据拆分成每天的天气情况,方便数据插入数据库
    55         n = 4
    56         sublist = [listall[i:i + n] for i in range(0, len(listall), n)]
    57         # 删除表头第一行
    58         sublist.remove(sublist[0])
    59         # 将列表元素中的最高和最低气温拆分,方便后续数据分析,并插入城市代码
    60 
    61         for sub in sublist:
    62             if sub == sublist[0]:
    63                 pass
    64             sub2 = sub[2].split('/')
    65             sub.remove(sub[2])
    66             sub.insert(2, sub2[0])
    67             sub.insert(3, sub2[1])
    68             sub.insert(0, cityname)
    69 
    70             Weather = WeatherItem()   #使用items中定义的数据结构
    71 
    72             Weather['cityname'] = sub[0]
    73             Weather['data'] = sub[1]
    74             Weather['tq'] = sub[2]
    75             Weather['maxtemp'] = sub[3]
    76             Weather['mintemp'] = sub[4]
    77             Weather['fengli'] = sub[5]
    78             yield Weather

    运行项目,即可获取数据,至此,我们完成了天气数据的抓取项目。

    项目完整代码:

    https://gitee.com/liangxinbin/Scrpay/tree/master/scrapymodel

  • 相关阅读:
    国内最火的3款前端开发框架
    Cordova是做什么的
    老师你好。使用cordova生成的hellowold 的安卓5.0版本太高。怎么才可以生成4.4的呢?
    一个类似bootstrap的foundation
    role在标签中的作用是什么?
    如何做到根据不同的进度用不同的颜色显示整个进度条
    wall 和panel有啥区别
    git ignore
    eclipse js 引用跳转
    计划
  • 原文地址:https://www.cnblogs.com/lxbmaomao/p/10367181.html
Copyright © 2011-2022 走看看