一 爬虫简介
爬虫就是通过编写程序模拟浏览器上网,然后让其去互联网上抓取数据的过程。
能实现爬虫的编程语言:
- 1.php:可以实现爬虫。php被号称是全世界最优美的语言(当然是其自己号称的,就是王婆卖瓜的意思),但是php在实现爬虫中支持多线程和多进程方面做的不好。
- 2.java:可以实现爬虫。java可以非常好的处理和实现爬虫,是唯一可以与python并驾齐驱且是python的头号劲敌。但是java实现爬虫代码较为臃肿,重构成本较大。
- 3.c、c++:可以实现爬虫。但是使用这种方式实现爬虫纯粹是是某些人(大佬们)能力的体现,却不是明智和合理的选择。
- 4.python:可以实现爬虫。python实现和处理爬虫语法简单,代码优美,支持的模块繁多,学习成本低,具有非常强大的框架(scrapy等)且一句难以言表的好!
爬虫的分类:
- 1.通用爬虫:通用爬虫是搜索引擎(Baidu、Google、Yahoo等)“抓取系统”的重要组成部分。主要目的是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份。 简单来讲就是尽可能的;把互联网上的所有的网页下载下来,放到本地服务器里形成备分,在对这些网页做相关处理(提取关键字、去掉广告),最后提供一个用户检索接口。
- 2.聚焦爬虫:聚焦爬虫是根据指定的需求抓取网络上指定的数据。例如:获取豆瓣上电影的名称和影评,而不是获取整张页面中所有的数据值。
robots.txt协议
如果自己的门户网站中的指定页面中的数据不想让爬虫程序爬取到的话,那么则可以通过编写一个robots.txt的协议文件来约束爬虫程序的数据爬取。robots协议的编写格式可以观察淘宝网的robots(访问www.taobao.com/robots.txt即可)。但是需要注意的是,该协议只是相当于口头的协议,并没有使用相关技术进行强制管制,所以该协议是防君子不防小人。
二 urllib模块介绍
python中自带的一个基于爬虫的模块
作用: 可以使用代码模拟浏览器发起请求。 request parse
使用流程:
- 指定一个URL
- 针对指定的url发起一个请求
- 获取服务器响应回来的页面数据
- 持久化存储
1 第一个urllib程序
# 需求爬取搜狗首页的页面数据 import urllib.request # 目标url url = 'https://www.sogou.com/' # 发起请求且返回一个响应对象 response = urllib.request.urlopen(url=url) # 获取页面数据:read函数返回的就是响应对象中存储的页面数据 page_text = response.read() print(page_text) # 进行持久化存储 with open('./sougou.html', 'wb') as f: f.write(page_text) print('写入成功')
2 url编码处理
# 爬取指定词条所对应的页面数据 import urllib.request import urllib.parse url = 'https://www.sogou.com/web?query=' # url 当中不可以存在非ACCISS码的字符数据 # 编码 word = urllib.parse.quote("特朗普搅乱世界") url += word print(url) # 发请求 response = urllib.request.urlopen(url=url) print(word) # 获取页面数据 page_text = response.read() with open('./sougou2.html', 'wb') as f: f.write(page_text)
3 反爬机制UA:
网站检查请求的UA,如果发现UA是爬虫程序,则拒绝提供网站数据
User-Agent:请求载体的身份标识
反反爬虫机制:伪装爬虫程序请求的UA
import urllib.request url = 'http://www.baidu.com/' # UA伪装 # 1. 自制定一个请求对象 headers = { # 存储任意的请求头信息 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' } # 该请求对象进行了成功的伪装 request = urllib.request.Request(url=url, headers=headers) # 2 针对自定制的请求对象发起请求 response = urllib.request.urlopen(request) page_text = response.read() print(page_text)
4 urlb模块的post请求
import urllib.request import urllib.parse url = 'https://fanyi.baidu.com/sug' # postq请求携带的参数进行处理 # 1. 将post请求参数封装到字典中 data = {'kw':'西瓜'} # 2. 使用parse模块中的urlencode进行编码处理,返回为str类型 data = urllib.parse.urlencode(data) type(data) # 字符串类型 # 3. 将步骤2的编码结果转换成byte类型 data = data.encode() # 发起post请求 urlopen函数的data参数表示的就是经过处理后的post请求携带的参数 response = urllib.request.urlopen(url=url, data=data) res_data = response.read() print(res_data)
三 Requests模块的使用
1 requests模块简介
requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求。功能强大,用法简洁高效。在爬虫领域中占据着半壁江山的地位。
为什么要使用requests模块
- 手动处理url编码
- 手动处理post请求参数
- 处理cookie和代理操作繁琐
使用requests模块:
- 自动处理url编码
- 自动处理post请求参数
- 简化cookie和代理操作
2 requests-get请求:
- 需求:爬取搜狗首页的页面数据
import requests url = 'https://www.sogou.com/' # 发起get请求:get方法会返回请求成功后的响应对象 response = requests.get(url=url) # 获取响应中的数据值:text可以获取响应对象中字符串形式的页面数据 page_data = response.text # 持久化操作 with open('sougou-request.html', 'w', encoding='utf-8') as fp: fp.write(page_data)
3 requests的response常用属性
import requests url = 'https://www.sogou.com/' # 发起get请求:get方法会返回请求成功后的响应对象 response = requests.get(url=url) # content获取的是response对象中二进制(byte)类型的页面数据 response.content # 返回一个响应状态码 response.status_code # 响应头信息 response.headers # 获取请求的url response.url
4 携带参数的get请求
# 方式一
import requests url = 'https://www.sogou.com/web?query=苹果&ie=utf8' request.get(url=url) page_text = response.text with open('./query.html', 'w', encoding='utf-8') as fp: fp.write(page_text)
# 方式二
import requests url = 'https://www.sogou.com/web' # 将参数封装到字典中 params = { 'query':'apple', 'ie':'utf-8' } request.get(url=url, params=params) page_text = response.text with open('./query.html2', 'w', encoding='utf-8') as fp: fp.write(page_text)
5 request模块get自定义请求头
import requests url = 'https://www.sogou.com/web' # 将参数封装到字典中 params = { 'query':'apple', # 自定义请求信息 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' } response = request.get(url=url, params=params, headers=headers) page_text = response.text with open('./query.html2', 'w', encoding='utf-8') as fp: fp.write(page_text)
6 request模块的post请求
import requests url = 'https://accounts.douban.com/j/mobile/login/basic' # 封装post请求参数 param_data = { 'ck': 'dFf2', 'name': '18629090745', 'password': 'cs1993413' , 'remember': 'false', 'ticket':'' } # 自动以请求头信息 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' } # 发起post请求 response = requests.post(url=url, data=param_data, headers=headers) # 获取响应对象中的页面数据 page_data = response.text
7 requests模块的ajax请求
(1) get请求
import requests url = 'https://movie.douban.com/j/chart/top_list' # 封装ajax的get请求中的参数 params = { 'type': '24', 'interval_id': '100:90', 'action': '', 'start': '60', 'limit': '20', } headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' } response = requests.get(url=url, params=params, headers=headers) page_data = response.text
(2) post请求
# 需求:爬取肯德基城市位置数据
#指定ajax-post请求的url(通过抓包进行获取) url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword' #定制请求头信息,相关的头信息必须封装在字典结构中 headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } #定制post请求携带的参数(从抓包工具中获取) data = { 'cname':'', 'pid':'', 'keyword':'北京', 'pageIndex': '1', 'pageSize': '10' } #发起post请求,获取响应对象 response = requests.post(url=url,headers=headers,data=data) #获取响应内容:响应内容为json串 print(response.text)
8 综合练习
# 需求:爬取搜狗某一个词条对一定范围页码表示的页面数据
import requests import os # 创建一个文件夹 if not os.path.exists('./pages'): os.mkdir('./pages') word = input('enter a word:') # 动态指定页面范围 start_pageNum = int(input('enter a start pageNum:')) end_pageNum = int(input('enter a end pageNum:')) url = 'https://www.sogou.com/web' headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } for page in range(start_pageNum, end_pageNum+1): params = { 'query':word, 'page':page, 'ie': 'utf-8' } response = requests.get(url=url, params=params, headers=headers) # 获取响应中的页面数据(指定页面(page)) page_text = response.text # 进行持久化存储 filenName = word+str(page)+'.html' filePath = 'pages/' + filenName with open(filePath, 'w', encoding='utf-8') as fp: fp.write(page_text)
9 request模块的cookie操作
实现流程:
- 1 执行登录操作(获取cookie)
- 2 在发起个人主页请求时,需要将cooki携带到请求中
注意: session对象:发送请求(会将cookie对象进行自动存储)
import requests session = requests.session() # 发起登录请求, 将cookie获取, 并且存储到session对象中 login_url = 'https://accounts.douban.com/j/mobile/login/basic' param_data = { 'ck': '', 'name': '18629090745', 'password': 'cs1993413' , 'remember': 'false', 'ticket':'' } headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } # 使用session发起post请求 login_response = session.post(url=login_url, data=data, headers=headers ) # 对个人主页发起请求(session), 获取响应页面数据 url = 'https://www.douban.com/people/181322653/' response = session.get(url=url, headers=headers) page_text = response.text
10 request模块的代理操作
免费代理ip的网站提供商:
- www.goubanjia.com
- 快代理
- 西祠代理
import requests headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } url = 'http://www.baidu.com/s?wd=ip&ie=utf-8' # 将代理ip封装到字典中 proxy = { 'http':'123.117.38.29:9000' } # 更换网络ip response = requests.get(url=url, proxies=proxy, headers=headers) with open('./daili.html', 'w', encoding='utf-8') as fp: fp.write(response.text)
四 图片验证码处理
云打码平台处理的实现流程:
- 1. 对携带验证码的页面数据进行抓取
- 2. 可以将页面数据种验证码进行解析, 验证码图片下载到本地
- 3. 可以将验证码图片提交给三方平台识别,返回验证码图片上的数据值
- 云打码平台:
- 在官网中注册(普通用户和开发者用户都需注册)
- 登陆开发者用户
- 实例代码的下载(开发者文档>>调用实例及最新的DLL>>pythonHTTP示例)
- 创建一个软件: 我的软件>> 添加新的软件
- 使用示例代码中的源码文件中的代码进行修改, 让其识别验证码图片中的数据
- 云打码平台:
调用云打码识别的相关接口对指定的验证码图片进行识别,返回图片上的数据值:
def getCode(codeImg): # 云打码平台普通用户用户名 username = 'cs4224485' # 密码 password = 'cs1993413' # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得! appid = 7817 # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得! appkey = '7d884e469f76fb1048bd0bb1b4e4bb87' # 验证码图片文件 filename = codeImg # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html codetype = 3000 # 超时时间,秒 timeout = 60 # 检查 if (username == 'username'): print('请设置好相关参数再测试') else: # 初始化 yundama = YDMHttp(username, password, appid, appkey) # 登陆云打码 uid = yundama.login(); print('uid: %s' % uid) # 查询余额 balance = yundama.balance(); print('balance: %s' % balance) # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果 cid, result = yundama.decode(filename, codetype, timeout); print('cid: %s, result: %s' % (cid, result)) return result
实现代码:
import requests import json import time from lxml import etree headers = { #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数 'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36', } url = 'http://www.yundama.com/' page_text = requests.get(url=url, headers=headers).text # 将页面数据种验证码解析, 验证码图片下载到本地 tree = etree.HTML(page_text) codeImg_url = tree.xpath('//*[@id="verifyImg"]')[0] # 获取了验证码图片对应的二进制数据值 code_img = requests.get(url=codeImg_url, headers=headers).content with open('./code.png', 'wb') as fp: fp.write(code_img) # 获得了验证码图片上的数据值 codeText = getCode('./code.png') # 进行登陆操作 post_url = '' data = { 'ck': codeText, 'name': '18629090745', 'password': 'cs1993413' , 'remember': 'false', 'ticket':'' }