【python学习笔记】35:爬虫基础和相关产品API(和风天气)使用实例

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/83831019

学习《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)

运行结果:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/83831019
今日推荐