版权声明:本文为博主原创学习笔记,如需转载请注明来源。 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)
运行结果: