1. 程式人生 > >【python學習筆記】35:爬蟲基礎和相關產品API(和風天氣)使用例項

【python學習筆記】35:爬蟲基礎和相關產品API(和風天氣)使用例項

學習《Python3爬蟲、資料清洗與視覺化實戰》時自己的一些實踐。


在網站URL後面跟robots.txt一般就可以看到網站允許和禁止爬取的資源。

GET請求獲取響應內容

最基本的爬蟲。

import requests

'''
中國旅遊網 /www.cntour.cn
'''

url = 'http://www.cntour.cn'
response = requests.get(url)  # 用GET方式獲取訪問該網站的響應
# print(type(response))  # <class 'requests.models.Response'>
# print(type(response.text)) # <class 'str'> print(response.text) # 其中包含了HTML字串

POST請求有道翻譯服務

這裡涉及偽裝成瀏覽器訪問和使用代理池,如果實在難攻克的話可以用time.sleep()做延時。

在Chrome的Network裡過濾,找XHR型別,即通過XMLHttpRequest方法傳送的請求,是用Ajax方式傳送的請求。在使用有道翻譯時,不按翻譯鍵也會隨著輸入的內容而自動翻譯,顯然是在用Ajax方式互動。

import requests
import json

'''
有道翻譯 http://fanyi.youdao.com/
西刺代理 http://www.xicidaili.com/
'''
# 使用有道翻譯傳送post請求來翻譯文字 def get_translate_data(word=None): # 訊息頭中的請求網址,因為有道反爬機制,在網上找到解決方案去掉"translate"後的"_o" url = 'http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule' # post的請求實體,在chrome的Header裡可以看到 form_data = {'i': word, # 這裡是要翻譯的單詞 'from': 'AUTO'
, 'to': 'AUTO', 'smartresult': 'dict', 'client': 'fanyideskweb', 'salt': '1540867058355', 'sign': 'a45461db88c2a4dcec5882c5d9670a20', 'doctype': 'json', 'version': '2.1', 'keyfrom': 'fanyi.web', 'action': 'FY_BY_REALTIME', 'typoResult': 'false'} # 構造一個瀏覽器的請求頭,偽裝成瀏覽器訪問,只要提供User-Agent(使用者代理),這裡偽裝成了Chrome headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'} # 代理池,可以到西刺代理裡找HTTP和HTTPS的ip proxies = { 'https': 'https://36.110.14.186:3128', 'http:': 'http://58.53.128.83:3128' } # 傳送post請求,獲得響應.要傳入請求實體,這裡還傳入了偽裝的請求頭和代理池 response = requests.post(url, data=form_data, headers=headers, proxies=proxies) # 將json格式字串轉字典 context = json.loads(response.text) # 列印翻譯後的資料 print(context['translateResult'][0][0]['tgt']) if __name__ == '__main__': get_translate_data('我是個大傻逼') # I'm a big silly force

BeautifulSoup網頁解析器

可以將網頁HTML解析成DOM樹的結構化資料,這項技術用來獲取想要的那部分資料,畢竟大多數網頁結構是比較複雜的。還好有檢查元素Copy Selector這兩樣功能。

from bs4 import BeautifulSoup
import requests
import re

'''
中國旅遊網 /www.cntour.cn
'''

url = 'http://www.cntour.cn/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'lxml')  # 使用LXML的HTML解析器
print(type(soup))  # <class 'bs4.BeautifulSoup'>
# 空格不能少,不然會解析錯誤;注意,即便是取單條,nth-child改為nth-of-type
# 這部分用於選擇的字串在chrome中右鍵檢查,然後複製selector
data = soup.select('#main > div > div.mtop.firstMod.clearfix > div.centerBox > ul.newsList > li > a')
print(type(data))  # <class 'list'>
# 把資料提取出來
for item in data:
    # print(type(item))  # <class 'bs4.element.Tag'>
    result = {
        'title': item.get_text(),  # 提取標籤的正文
        'link': item.get('href'),  # 提取標籤的href屬性L(連結字串)
        'ID': re.findall('\d+', item.get('href'))  # 用正則從連結字串中匹配數字部分
    }
    print(result)

用API爬取天氣預報資料

其實就是向指定格式的URL發請求就好了。這裡應當重點關注一下加密簽名的使用方式,這部分書上沒講。使用提供的加密簽名可以防止自己的key暴露給第三方,被惡意使用。

另外在實際寫程式碼時候發現了URL編碼的問題,在這個爬蟲裡只要考慮+號的編碼問題。

import base64
import csv
import hashlib
import time
import pymongo
import requests

'''
和風天氣3-10天天氣預報API https://www.heweather.com/douments/api/s6/weather-forecast
和風天氣加密簽名認證 https://www.heweather.com/documents/api/s6/sercet-authorization
JSON線上結構化工具 http://www.json.org.cn/tools/JSONEditorOnline/index.htm
和風天氣控制檯(看剩餘訪問量和key和使用者ID) https://console.heweather.com/my/service
城市程式碼ID下載 http://www.heweather.com/documents/city
'''

mykey = '80ba7933049a4065b687893a7619e909'
city_id = []  # 記錄城市id的列表


# API的一般使用方式
def weather_api(location, key):
    url = 'https://free-api.heweather.com/s6/weather/forecast?location=' + location + '&key=' + key
    response = requests.get(url)
    response.encoding = 'utf8'  # 設定Respons物件的編碼方式
    # print(response.text)
    return response


# 和風天氣簽名生成演算法-Python版本
# params API呼叫的請求引數集合的字典(全部需要傳遞的引數組成的字典),不包含sign引數
# secret 使用者的認證 key
# return string 返回引數簽名值
def gen_sign(params, secret):
    canstring = ''
    # 先將引數以其引數名的字典序升序進行排序
    # 字典的items()函式以列表返回可遍歷的(鍵,值)元組陣列
    params = sorted(params.items(), key=lambda item: item[0])
    # 遍歷排序後的引數陣列中的每一個key/value對
    for k, v in params:
        # 不能包含sign或者key或者空字串key.sign是要計算生成的,而key只用來計算sign,防止暴露給第三方
        if k != 'sign' and k != 'key' and v != '':
            canstring += k + '=' + v + '&'  # URL裡面的GET引數就是這樣的
    canstring = canstring[:-1]  # 用切片去除最後多餘的一個'&'符
    canstring += secret  # 尾接key
    # 在MD5物件建立前需要對資料進行編碼
    canstring = canstring.encode('utf8')
    # 用這個拼接後的字串建立一個md5物件,然後digest()方法返回md5的byte格式
    md5 = hashlib.md5(canstring).digest()
    # 對其進行base64編碼並返回,現在返回的就是加密簽名sign的value了
    return base64.b64encode(md5)  # 這個方法需要的是一個byte物件


# API的加密簽名使用方式
def weather_api_sign(location, key, username='HE1811071811441319'):
    # 獲取10位時間戳,即取當前time()結果的整數部分
    timestamp = int(time.time())
    # 請求引數,不包含key也不包含sign
    params = {'location': location, 'username': username, 't': str(timestamp)}
    # 計算簽名,並新增到請求引數字典中.注意byte轉換成字串才能用於後面的字串拼接
    sign = gen_sign(params, key)
    sign = str(sign, 'utf8')
    # 我發現有時會出現{"HeWeather6":[{"status":"sign error"}]}即簽名不正確
    # 這是因為sign裡可能出現'+'號導致的,在傳輸時'+'會被視為' ',因此需要做url轉義成'%2b'
    sign = str.replace(sign, '+', '%2B')
    params['sign'] = sign  # 現在得到的就是正確的簽名了,將其放入引數字典中
    # 從字典構造URL,然後對其傳送GET請求
    url = 'https://free-api.heweather.com/s6/weather/forecast?'
    for k, v in params.items():
        url += k + '=' + v + '&'
    url = url[:-1]  # 去除最後多餘的一個'&'符
    # print(url)
    response = requests.get(url)
    response.encoding = 'utf8'  # 設定Respons物件的編碼方式
    # print(response.text)
    return response


# 從下載的csv檔案中讀取城市id列表
def city_id_csv():
    global city_id
    # 解決'gbk' codec can't decode byte 0xad in position 256: illegal multibyte sequence
    csv_file = csv.reader(open('china-city-list.csv', 'r', encoding='UTF-8'))
    # print(type(csv_file))  # <class '_csv.reader'>
    # 用列舉使在for-in迴圈中能考察其迴圈次數
    for i, line in enumerate(csv_file):
        if i > 1:  # 這是為了跳過前兩行表頭
            city_id.append(line[0])
        if i > 30:  # 不妨只取少量城市,只是實現功能(現在免費使用者一天最多才1000次天氣查詢)
            break


# 連線到本地MongoDB中的資料庫(如果不存在即建立),並向其中寫入資料.要先呼叫填充city_id列表的函式
def sto_in_local_mongo():
    # 連線到本地MongoDB
    client = pymongo.MongoClient('localhost', 27017)
    # 天氣資料庫
    db_weather = client['weather']
    # 天氣資料庫中的資料表
    sheet_weather = db_weather['sheet_weather_3']
    # 寫入
    for id in city_id:
        city_weather = weather_api_sign(id, mykey)
        dic = city_weather.json()  # Response物件的json格式以字典儲存
        print(dic)
        sheet_weather.insert_one(dic)  # 寫入資料庫
        time.sleep(1)


if __name__ == '__main__':
    # weather_api('CN101010100', mykey)
    # weather_api_sign('CN101010100', mykey)
    # city_id_csv()  # 從本地csv檔案讀取城市編號
    # sto_in_local_mongo()  # 查詢城市天氣並存儲到本地
    # 從本地使用資料庫中的資料
    Client = pymongo.MongoClient('localhost', 27017)
    Db_Weather = Client['weather']
    Sheet_Weather = Db_Weather['sheet_weather_3']
    # 如查詢北京的資料如下.這裡的'.0'可以省略
    for item in Sheet_Weather.find({'HeWeather6.0.basic.parent_city': '北京'}):
        print(item)

執行結果:
在這裡插入圖片描述