【python學習筆記】35:爬蟲基礎和相關產品API(和風天氣)使用例項
阿新 • • 發佈:2018-11-19
學習《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)
執行結果: