1、爬虫介绍
什么是爬虫:
通过编写程序,模拟浏览器上网,然后让其去互联网上爬取数据的过程。
爬虫在使用场景中的分类:
通用爬虫:
抓取系统重要组成部分。抓取的是一整张页面数据。
聚焦爬虫:
是建立在通用爬虫的基础之上。抓取的是页面中特定的局部内容。
增量式爬虫:
检测网站中数据更新的情况。只会抓取网站中最新更新出来的数据。
反爬机制:
门户网站,可以通过制定相应的策略或者技术手段,防止爬虫程序进行网站数据的爬取。
反反爬机制:
爬虫程序可以通过制定相关的策略或者技术手段,破解门户网站中具备的反爬机制,从而可以获取门户网站的数据。
robots.txt协议:
君子协议。规定了网站中哪些数据可以被爬虫爬取哪些数据不可以被爬取。
http协议
概念:就是服务器和客户端进行数据交互的一种形式。
常用请求头信息
User-Agent:请求载体的身份标识
Connection:请求完毕后,是断开连续还是保持连接
常用响应头信息
Content-Type:服务器响应回客户端的数据类型
https协议:
安全的超文本传输协议
加密方式:
对称密钥加密
非对称密钥加密
证书密钥加密
2、requests模块
urllib模块
requests模块
requests模块:python中原生的一款基于网络请求的模块,功能非常强大,简单便捷,效率极高。
作用:模拟浏览器发起请求。
如何使用:(requests模块的编码流程)
1、指定url
2、发起请求
3、获取响应数据
4、持久化存储
环境安装:
pip install requests
实战编码:
需求:爬取搜狗首页的页面数据
import requests
if __name__ == "__main__":
url = 'https://www.sogou.com/'
response = requests.get(url=url)
page_text = response.text
print(page_text)
with open('./sogou.html','w',encoding='utf-8') as fp:
fp.write(page_text)
print("爬取结束")
UA:User-Agent(请求载体的身份标识)
UA检测:门户网站的服务器会检测对应请求的载体身份标识,如果检测到请求的载体身份标识为某一款浏览器,
说明该请求是一个正常的请求。但是,如果检测到的请求的载体身份标识不是基于某一款浏览器的,则表示该请求
为不正常的请求(爬虫),则服务器端就很有可能拒绝该次请求。
UA伪装:让爬虫对应的请求载体身份标识伪装成某一款浏览器。
实战编码:
需求:网页采集器
#爬取搜狗指定词条对应的搜索结果页面(简易网页采集器)
import requests
if __name__=="__main__":
#UA伪装:将对应的User-Agent封装到一个字典中
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
url = 'https://www.sogou.com/web'
kw = input('enter a word')
#处理url携带的参数:封装到字典中
param = {
'query':kw
}
response = requests.get(url=url,params=param,headers=headers)
page_text = response.text
fileName = kw+'.html'
with open(fileName,'w',encoding='utf-8') as fp:
fp.write(page_text)
print(fileName,'保存成功!')
实战编码:
需求:百度翻译
import requests
import json
if __name__== "__main__":
#1、指定url
post_url = 'https://fanyi.baidu.com/sug'
#2、进行UA伪装
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
#3、post请求参数处理(同get请求一致)
word = input('enter a word:')
data = {
'kw':word
}
#4、请求发送
response = requests.post(url=post_url,data=data,headers=headers)
#5、获取响应数据:json()方法返回的是obj
dic_obj = response.json()
#持久化存储
fileName = word+'.json'
fp = open(fileName,'w',encoding='utf-8')
json.dump(dic_obj,fp=fp,ensure_ascii=False)
print("over!")
实战编码:
需求:豆瓣电影爬取
import requests
import json
if __name__=="__main__":
url = 'https://movie.douban.com/j/chart/top_list'
param = {
'type':'24',
'interval_id':'100:90',
'action':'',
'start':'1',#从库中的第几部电影去取
'limit': '20'#一次取出的个数
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
response = requests.get(url=url,params=param,headers=headers)
list_data = response.json()
fp = open('./douban.json','w',encoding='utf-8')
json.dump(list_data,fp=fp,ensure_ascii=False)
print('over!')
实战编码:
需求:餐厅位置爬取
import requests
if __name__=="__main__":
url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'
param = {
'cname':'',
'pid':'',
'keyword': '海口',
'pageIndex':'1',
'pageSize': '10'
}
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
response = requests.post(url=url,params=param,headers=headers)
page_text = response.text
with open('./kfc.html','w',encoding='utf-8') as fp:
fp.write(page_text)
print('kfc.html','over')
实战编码:
需求:爬取国家药品监督管理总局基于中华人民共和国化妆品生产许可证相关数据
-动态加载数据
-首页中对应的企业信息数据是通过ajax动态请求到的。
-对比url的观察发现:
-url的域名都是一样的,只有携带的参数(id)不一样
-id值可以从首页对应的ajax请求到的json串中获取
-域名和id值拼接出一个完整的企业对应的详情页的url
-详情页的企业详情数据也是动态加载出来的
-所有的post请求的url都是一样的,只有参数id值是不同
-如果我们可以批量获取多家企业的id后,就可以将id和url形成一个完整的详情页对应详情数据的ajax请求的url
import requests
import json
if __name__=="__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
id_list = [] # 储存企业的id
all_data_list = [] # 存储所有的企业详情数据
url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsList'
for page in range(1,6):
page = str(page)
data = {
'on': 'true',
'page': page,
'pageSize': '15',
'productName':'',
'conditionType':'1',
'applyname':'',
'applysn':'',
}
json_ids=requests.post(url=url,headers=headers,data=data).json()
for dic in json_ids['list']:
id_list.append(dic['ID'])
#获取企业详情数据
post_url = 'http://scxk.nmpa.gov.cn:81/xk/itownet/portalAction.do?method=getXkzsById'
for id in id_list:
data = {
'id':id
}
detail_json = requests.post(url=post_url,headers=headers,data=data).json()
all_data_list.append(detail_json)
#持久化存储all_data_list
fp = open('./allData.json','w',encoding='utf-8')
json.dump(all_data_list,fp=fp,ensure_ascii=False)
print('over')
3、数据解析
聚焦爬虫:爬取页面中指定的页面内容。
- 编码流程:
- 指定url
- 发起请求
- 获取响应数据
- 数据解析
- 持久化存储
数据解析分类:
- 正则
- bs4
- xpath
数据解析原理概述:
- 解析的局部文本内容都会在标签之间或者标签对应的属性中进行存储
- 1.进行指定标签的定位
- 2.标签或者标签对应的属性中存储的数据值进行提取(解析)
爬取图片:
import requests
if __name__ == "__main__":
#爬取图片数据
url = 'http://c-ssl.duitang.com/uploads/item/202003/30/20200330091226_tW4YP.jpeg'
#content返回的是二进制形式的图片数据
#text(字符串) content(二进制) json()(对象)
img_data = requests.get(url=url).content
with open('./guitu.jpg','wb') as fp:
fp.write(img_data)
爬取糗事网图片:
import requests
import re
import os
#爬取糗事百科中糗图板块下所有的糗图图片
if __name__ == "__main__":
if not os.path.exists('./qiutuLibs'):
os.mkdir('./qiutuLibs')
url = 'https://www.qiushibaike.com/imgrank/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
#使用通用爬虫对url对应的一整张页面进行爬取
page_text = requests.get(url=url,headers=headers).text
#使用聚焦爬虫将页面中所有的糗图进行解析
ex = '<div class="thumb">.*?<img src="(.*?)" alt.*?</div>'
img_src_list = re.findall(ex,page_text,re.S)
for src in img_src_list:
#拼接出一个完整的图片url
src = 'https:'+src
#请求到了图片的二进制数据
img_data = requests.get(url=src,headers=headers).content
img_name = src.split('/')[-1]
#图片存储的路径
imgPath = './qiutuLibs/'+img_name
with open(imgPath,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!')
增加分页爬取图片
url = 'https://www.qiushibaike.com/imgrank/page/%d/'
#分页爬取
for pageNum in range(1,3):
#对应页面的url
new_url = format(url%pageNum)
#使用通用爬虫对url对应的一整张页面进行爬取
page_text = requests.get(url=new_url,headers=headers).text
bs4进行数据解析
- 数据解析的原理:
- 1.标签定位
- 2.提取标签、标签属性中存储的数据值
- bs4数据解析的原理:
-1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
-2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
- 环境安装:
- pip install bs4
- pip install lxml
- 如何实例化BeautifulSoup对象:
- from bs4 import BeautifulSoup
- 对象的实例化:
- 1.将本地的html文档中的数据加载到该对象中
fp = open(’./test.html’,‘r’,encoding=‘utf-8’)
soup = BeautifulSoup(fp,‘lxml’)
- 2.将互联网上获取的页面源码加载到该对象中
page_text = response.text
soup = BeatifulSoup(page_text,‘lxml’)
- 提供的用于数据解析的方法和属性:
- soup.tagName:返回的是文档中第一次出现的tagName对应的标签
- soup.find():
- find(‘tagName’):等同于soup.div
- 属性定位:
- soup.find(‘div’,class_/id/attr=‘song’)
- soup.find_all(‘tagName’):返回符合要求的所有标签(列表)
- select:
- select(‘某种选择器(id,class,标签…选择器)’),返回的是一个列表。
- 层级选择器:
- soup.select(’.tang > ul > li > a’): >表示的是一个层级
- soup.select(’.tang > ul a’): 空格表示的多个层级
- 获取标签之间的文本数据:
- soup.a.text/string/get_text()
- text/get_text():可以获取某一个标签中所有的文本内容
- string:只可以获取该标签下面直系的文本内容
- 获取标签中属性值:
- soup.a[‘href’]
实例:爬取三国演义小说所有的章节标题和章节内容 https://www.shicimingju.com/book/sanguoyanyi.html
import requests
from bs4 import BeautifulSoup
#爬取三国演义小说所有的章节标题和章节内容 https://www.shicimingju.com/book/sanguoyanyi.html
if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
url = 'https://www.shicimingju.com/book/sanguoyanyi.html'
#对首页的页面数据进行爬取
page_text = requests.get(url=url,headers=headers).text
#在首页中解析出章节的标题和详情页的url
#1.实例化BeautSoup对象,需要将页面源码数据加载到该对象中
soup = BeautifulSoup(page_text,'lxml')
#解析章节标题和详情页的url
li_list = soup.select('.book-mulu > ul > li')
fp = open('./sanguo.txt','w',encoding='utf-8')
for li in li_list:
title = li.a.string
detail_url = 'http://www.shicimingju.com'+li.a['href']
#对详情页发起请求,解析出章节内容
detail_page_text = requests.get(url=detail_url,headers=headers).text
#解析出详情页中相关的章节内容
detail_soup = BeautifulSoup(detail_page_text,'lxml')
div_tag = detail_soup.find('div',class_='chapter_content')
#解析到了章节的内容
content = div_tag.text
fp.write(title+':'+content+'\n')
print(title,'爬取成功!')
xpath进行数据解析:
- 最常用且最便捷高效的一种解析方式。通用性。
- xpath解析原理:
- 1.实例化一个etree的对象,且需要将被解析的页面源码数据加载到该对象中。
- 2.调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
- 环境的安装:
- pip install lxml
- 如何实例化一个etree对象:from lxml import etree
- 1.将本地的html文档中的源码数据加载到etree对象中:
etree.parse(filePath)
- 2.可以将从互联网上获取的源码数据加载到该对象中:
etree.HTML(‘page_text’)
- xpath(‘xpath表达式’)
- xpath表达式:
- /:表示的是从根节点开始定位。表示的是一个层级。
- //:表示的是多个层级。可以表示从任意位置开始定位。
- 属性定位://div[@class=‘song’] tag[@attrName="attrValueName=“attrValue”]
- 索引定位://div[@class=“song”]/p[3] 索引是从1开始的。
- 取文本:
- /text() 获取的是标签中直系的文本内容
- //text() 标签中非直系的文本内容(所有的文本内容)
- 取属性:/@attrName
实例:58二手房 https://haikou.58.com/ershoufang/
import requests
from lxml import etree
if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
#爬取到页面源码数据
url = 'https://haikou.58.com/ershoufang/'
page_text = requests.get(url=url,headers=headers).text
#数据解析
tree = etree.HTML(page_text)
#存储的就是li标签对象
li_list = tree.xpath('//ul[@class="house-list-wrap"]/li')
fp = open('58.txt','w',encoding='utf-8')
for li in li_list:
title = li.xpath('./div[2]/h2/a/text()')[0]
print(title)
fp.write(title+'\n')
实例:爬取4k图片 http://pic.netbian.com/4kmeinv/
import requests
from lxml import etree
import os
if __name__ == "__main__":
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
#爬取到页面源码数据
url = 'http://pic.netbian.com/4kmeinv/'
response = requests.get(url=url,headers=headers)
#手动设定响应数据的编码格式
#response.encoding = 'utf-8'
page_text = response.text
#数据解析:src的属性值 alt属性
tree = etree.HTML(page_text)
#存储的就是li标签对象
li_list = tree.xpath('//div[@class="slist"]/ul/li')
if not os.path.exists('./picLibs'):
os.mkdir('./picLibs')
for li in li_list:
img_src = 'http://pic.netbian.com'+ li.xpath('./a/img/@src')[0]
img_name = li.xpath('./a/img/@alt')[0] + '.jpg'
#通用处理中文乱码的解决方案
img_name = img_name.encode('iso-8859-1').decode('gbk')
#请求图片进行持久化存储
img_data = requests.get(url=img_src,headers=headers).content
img_path = 'picLibs/'+img_name
with open(img_path,'wb') as fp:
fp.write(img_data)
print(img_name,'下载成功!!!')
实例:爬取全国城市名称 https://www.aqistudy.cn/historydata/
import requests
from lxml import etree
if __name__ == "__main__":
# headers = {
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
# }
# #爬取到页面源码数据
# url = 'https://www.aqistudy.cn/historydata/'
# page_text = requests.get(url=url,headers=headers).text
#
# #数据解析
# tree = etree.HTML(page_text)
# host_li_list = tree.xpath('//div[@class="bottom"]/ul/li')
# all_city_names = []
# #解析到了热门城市的城市名称
# for li in host_li_list:
# hot_city_name = li.xpath('./a/text()')[0]
# all_city_names.append(hot_city_name)
#
# city_names_list = tree.xpath('//div[@class="bottom"]/ul/div[2]/li')
# # 解析到了所有城市的城市名称
# for li in city_names_list:
# city_name = li.xpath('./a/text()')[0]
# all_city_names.append(city_name)
#
# print(all_city_names,len(all_city_names))
#---------------------------------------------------------
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36'
}
#爬取到页面源码数据
url = 'https://www.aqistudy.cn/historydata/'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
#用或连接将二个标签同时解析
a_list = tree.xpath('//div[@class="bottom"]/ul/li/a | //div[@class="bottom"]/ul/div[2]/li/a')
all_city_names = []
for a in a_list:
city_name = a.xpath('./text()')[0]
all_city_names.append(city_name)
print(all_city_names,len(all_city_names))
4、验证码
验证码识别:
反爬机制:验证码.识别验证码图片中的数据,用于模拟登录操作。
识别验证码的操作:
- 人工肉眼识别。(不推荐)
- 第三方自动识别(推荐)
- 云打码:http://www.yundama.com/demo.html
云打码的使用流程:
- 注册:普通和开发者用户
- 登录:
- 普通用户的登录:查询该用户是否还有剩余的题分
- 开发者用户的登录:
- 创建一个软件:我的软件-》添加新软件-》录入软件名称-》提交(软件id和密钥)
- 下载示例代码:开发文档-》点此下载:云打码接口Dll-》PythonHTTP示例下载
(可以用超级鹰代替云打码)