赞
踩
爬虫:请求网站并提取数据的自动化程序。 通过编写程序,模拟浏览器上网,然后让其去互联网上抓取数据的过程。
目录
requests模块 (简单便捷,接下来都是使用的requests模块)
根据使用场景,爬虫可以分为三类:
1.通用爬虫(大而全)
功能强大,采集面广泛,通常用于搜索引擎,比如百度浏览器就是一个很大的爬虫程序。
2.聚焦爬虫(小而精)
建立在通用爬虫的基础上,抓取页面中特定的局部内容,比如说去某个网站批量获取某些数据。
3.增量式爬虫(只采集更新后的内容)
这其实是聚焦爬虫的一个迭代爬虫,检测网站中数据更新情况,只会抓取网站中最新更新的数据。
爬虫中有一个Robots协议,又称为“网络爬虫排除标准”,君子协议,规定了网站中哪些数据可以被爬虫爬取,哪些不可被爬取。一般情况下直接在网站首页网址后面加/robots.txt就能查看,比如百度的Robots协议就在https://www.baidu.com/robots.txt 。
http协议:就是服务器和客户端进行数据交互的一种形式
https协议:安全的超文本传输协议,采用证书密钥加密
常用请求头:User-Agent(请求载体的身份标识)
Connection(请求完毕后,是断开连接还是保持连接)
常用响应头:Content-Type(服务器响应回客户端的数据类型)
加密方式:1.对称密钥加密:客户端将要传输到服务器的参数加密,传输时携带密钥
2.非对称密钥加密:服务端创建密钥对,将公钥发给客户端,客户端使用密钥对消息加
密,将加密信息发给服务端,服务端再用私钥解密。
3.证书密钥加密:公开密钥-->证书认证机构-->给公钥签名(防伪)-->证书
urllib中包括了四个模块:urllib.request
, urllib.error
, urllib.parse
, urllib.robotparser
urllib.request可以用来发送请求request和获取请求request的结果
urllib.error包含了urllib.request产生的异常
urllib.parse用来解析和处理URL
urllib.robotparse用来解析页面的robots.txt文件
urllib.request 模块提供了最基本的构造 HTTP 请求的方法,利用它可以模拟浏览器的一个请求发起过程,同时它还带有处理 authenticaton (授权验证), redirections (重定向), cookies (浏览器Cookies)以及其它内容。
- import urllib.request #导入模块
- reaponse=urllib.request.urlopen("URL") #获取网页内容
- html=reaponse.read() #读取网页内容,放到html
- print(html) #内容以b开头,打印出来是二进制的数据
- html2=html.decode('utf-8') #解码
- print(html2)
requests模块:python中原生的一款基于网络请求到模块,功能强大,简单便捷,效率极高
作用:模拟浏览器发请求
1.指定url 2.发起请求 3.获取响应数据 4.持久化存储
- #爬取搜狗首页
-
- import requests
- # 1.指定url
- url='https://www.sogou.com/'
- # 2.发起请求,get方法会返回一个响应对象
- response=requests.get(url=url)
- # 3.获取响应数据 .text返回的是字符串形式的响应数据
- page_text=response.text
- print(page_text)
- # 4.持久化存储
- with open('./sogou.html','w',encoding='utf-8') as fp:
- fp.write(page_text)
- print('爬取数据结束')
User-Agent:请求载体的身份标识
UA伪装:如果门户网站的服务器会检测对于请求的载体身份标识,如果检测到请求的载体身份标识为某一款浏览器。
注:说明该请求是一个正常请求,但是如果检测到请求的载体身份标识不是基于某一款浏览器的,则表示该请求为不正常请求(爬虫),则服务器端很可能拒绝该次请求,所以在爬虫中进行UA伪装。
post请求(携带了参数),响应数据是一组json数据
- import json
- import requests
- #1.指定url
- post_url='https://fanyi.baidu.com/sug'
- #2.进行UA伪装
- headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- #3.post请求参数处理(同get请求一致)
- word=input('输入一个单词:')
- data={'kw':word}
- #4.请求发送
- response=requests.post(url=post_url,data=data,headers=headers)
- #5.获取响应数据
- response.text #获取到字符串类型数据
- dic_obj=response.json() #json()方法返回的是一个字典的对象obj(如果确定响应数据是json类型,才可以使用json())
- #6.持久化存储
- filename=word+'.json'
- fp=open(filename,'w',encoding='utf-8')
- json.dump(dic_obj,fp=fp,ensure_ascii=False) #(对象,文件,中文)
- print('over!!!!!!!')
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。易于人阅读和编写,可以在多种语言之间进行数据交换 [5] 。同时也易于机器解析和生成。
JSON是一个标记符的序列。这套标记符包含六个构造字符、字符串、数字和三个字面名。
JSON是一个序列化的对象或数组。
json.dump() 是把python对象转换成json对象生成一个fp的文件流,和文件相关。ensure_ascii=True:默认输出ASCLL码,如果把这个该成False,就可以输出中文。
Json语法: JSON 语法是 JavaScript 对象表示语法的子集。
Json对象:
JSON 对象在大括号 { } 中书写:
{key1 : value1, key2 : value2, ... keyN : valueN }
Json数组:
JSON 数组在中括号 [ ] 中书写:(数组可以包含多个对象)
[
{ key1 : value1-1 , key2:value1-2 },
{ key1 : value2-1 , key2:value2-2 },
{ key1 : value3-1 , key2:value3-2 },
...
{ key1 : valueN-1 , key2:valueN-2 },
]
JSON 是 JS 对象的字符串表示法,它使用文本表示一个 JS 对象的信息,本质是一个字符串。
如
var
obj={a:
'Hello'
, b:
'World'
};
//这是一个对象,注意键名也是可以使用引号包裹
var
json=
'{"a": "Hello", "b": "World"}'
;
//这是一个JSON字符串,本质是一个字符串
要实现从JSON字符串转换为JS对象,使用 JSON.parse() 方法:
var
obj = JSON.parse(
'{"a": "Hello", "b": "World"}'
);
//结果是 {a: 'Hello', b: 'World'}
要实现从JS对象转换为JSON字符串,使用 JSON.stringify() 方法:
var
json = JSON.stringify({a:
'Hello'
, b:
'World'
});
//结果是 '{"a": "Hello", "b": "World"}''
滑动滚轮,当滑到底部会加载一组新的电影数据,地址栏(url)没变是做的一个页面的局部刷新,是发起了Ajax(阿贾克斯)请求
- import json
- import requests
- url='https://movie.douban.com/j/chart/top_list'
- #将参数封装到字典
- param={
- 'type':'24',
- 'interval_id':'100:90',
- 'action':'',
- 'start':'0', #从库中第几部电影开始取
- 'limit':'20', #一次取多少部电影
- }
- headers={'User-Agent':
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- 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!!!!!')
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。
AJAX 通过在后台与服务器进行少量数据交换,使网页实现异步更新。这意味着可以在不重载整个页面的情况下,对网页的某些部分进行更新。
数据解析原理:解析的局部的内容都会在标签之间或者标签对应的属性中进行存储
1.进行指定标签的定位
2.标签或者标签对应的属性中存储的数据值进行提取(解析)
聚焦爬虫,是爬取页面中指定的页面内容。首先爬整张页面内容,再将指定内容提取出来,提取过程就叫数据解析。
编码流程:1.指定url
2.发起请求
3.获取响应数据
4.数据解析
5.持久化存储
数据解析分类:正则、bs4、xpath
- import requests
- url='https://wx1.sinaimg.cn/orj360/001DQGfkgy1h46mjn19khj61o02yo1ky02.jpg'
- #content返回的是二进制的图片数据
- # text (字符串) content (二进制) json() (对象)
- img_data=requests.get(url=url).content
- with open('./tupian.jpg','wb') as fp: # w以写方式打开文件,b以二进制方式打开文件(不能单独使用,
- 以wb或者rb组合使用)
- fp.write(img_data)
- import re
- import requests
- import os
- #创建一个文件夹,保存所有图片
- if not os.path.exists('./tupian'):
- os.mkdir('./tupian')
- url='https://tieba.baidu.com/f'
- header={'User-Agent':
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- #使用通用爬虫,对整张页面进行爬取
- page_text=requests.get(url=url,headers=header).text
- #使用聚焦爬虫对页面中所有图片进行提取(解析)
- ex='<div class="card_banner card_banner_link"><img src="(.*?)”id.*?</div>'
- img_src_list=re.findall(ex,page_text,re.S)
- #print(img_src_list)
- #拼接一个完整的图片url
- for src in img_src_list:
- src='http://'+src
- #请求到图片的二进制数据
- img_data=requests.get(url=src,headers=header).content
- #生成图片名称
- img_name=src.split('/')[-1]
- imgPath='./tupian'+img_name
- with open(imgPath,'wb') as fp:
- fp.write(img_data)
- print(img_data,'下载成功')
数据解析原理:1.标签定位 2.提取标签、标签属性中存储的数据值
bs4数据解析原理:
1.实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中
2.通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取
(需要安装bs4、lxml环境)
- from bs4 import BeautifulSoup
- #对象实例化:
- # 1.将在本地的html文档中的数据加载到该对象中
- fp=open('./test.html','r',encoding='utf-8')
- soup= BeautifulSoup(fp,'lxml')
- # 2.将互联网上获取的页面源码加载到该对象中
- page_text=response.text
- soup=BeautifulSoup(page_text,'lxml')
(1)soup.tagName:返回的是文档中第一次出现的tagName对应的标签
(2)soup.find():
—find('tagName'):等同于soup.div
—属性定位:suop.find('div',class_/id/attr='song')
(3)soup.find_all('tagName'):返回符号要求的所有标签(列表)
(4)select:
select('某种选择器 ( id,class,标签...选择器 ) '),返回的是一个列表
层级选择器:
—soup.select('.tang>ul>li>a') :>表示的是一个层级
—soup.select('.tang> ul a ') :空格表示的多个层级
- from bs4 import BeautifulSoup
- fp=open('./test.html','r',encoding='utf-8')
- soup= BeautifulSoup(fp,'lxml')
- print(soup)
- print(soup.a) #soup.tagName 返回的是html中第一次出现的tagName标签
- print(soup.div)
- print(soup.find('div')) #等同于print(soup.div)
- print(soup.find('div',class_='song'))
- print(soup.find_all('a'))
- print(soup.select('.tang'))
- print(soup.select('.tang>ul>li>a')[0])
- print(soup.select('.tang>ul a')[0])
soup.a.text /string /get_text()
text /get_text() :可以获取某一个标签中所有的文本内容
(string:只可以获取该标签下面直系的文本内容)
- print(soup.find('div',class_='song').text) #可以获得div下的所有内容(返回字符串)
- print(soup.find('div',class_='song').string) #只能获取div下的直系内容
soup.a [ ' href属性名称 ' ]
print(soup.select('.tang>ul a')[0]['href']) #获取a的属性值
爬取三国演义小说所有的章节标题和章节内容
- import requests
- from bs4 import BeautifulSoup
- url='https://sanguo.5000yan.com/'
- header={'User-Agent':
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- #对首页数据进行爬取
- page_text=requests.get(url=url,headers=header).text
- #在首页中解析出章节的标题和详情页的url
- #1.实例化BeautifulSoup对象,需要将页面源码数据加载到该对象中
- soup=BeautifulSoup(page_text,'lxml')
- #解析章节标题和详情页的url
- li_list=soup.select('.sidamingzhu-list-mulu>ul>li')
- fp=open('./sanguo.txt','w',encoding='utf-8')
- for li in li_list:
- title=li.a.string
- detail_url=li.a['href']
- #对详情页发起请求,解析出章节内容
- detail_page=requests.get(url=detail_url,headers=header).text
- #解析出详情页中相关的章节内容
- detail_soup=BeautifulSoup(detail_page,'lxml')
- div_tag=detail_soup.find('div',class_='grap')
- #解析到了章节的内容
- content=div_tag.text
- fp.write(title+':'+content+'\n')
- print(title,'爬取成功')
-
xpath解析原理:
1.实例化一个etree对象,且需要将被解析的页面源码数据加载到该对象中。
2.调用etree对象中的xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
- from lxml import etree
-
- 1.将本地的html文档中的源码数据加载到etree对象中:
- etree.parse(filePath)
- 2.将从互联网上获取的源码数据加载到该对象中
- etree.HTML('page_text')
- xpath('xpath表达式')
(1) / :表示的是从根节点开始定位。表示的是一个层级。
(2) // :表示的是多个层级。可以表示从任意位置开始定位。
(3)属性定位://div[@class='song'] tag[@atrrName="attrValue"]
(4)索引定位://div[@class="song"]/p[3] 索引是从1开始的
(5)取文本:
/text() 获取的是标签中直系的文本内容
//text() 标签中非直系的文本内容(所有文本内容)
(6)取属性:
/@attrName ==>img/src
- from lxml import etree
- #实例化好了一个etree对象,且将被解析的源码加载到了该对象中
- tree=etree.parse('test.html')
- r=tree.xpath('/html/body/div')
- r1=tree.xpath('/html//div') #这三个表达式,返回结果一样,所以表达的含义一样
- r2=tree.xpath('//div')
- r3=tree.xpath('//div[@class="song"]/p[3]')
- #r4=tree.xpath('//div[@class="tang"]//li[5]/a')#标签定位
- r4=tree.xpath('//div[@class="tang"]//li[5]/a/text()')[0] #获取标签内文本内容,得到的是一个列表,用[0]取出内容
- r5=tree.xpath('//li[7]//text()') #这里用/text()不能获取文本,因为这个内容没有直系存储在li标签,但是用//text()就可以获取到
- r6=tree.xpath('//div[@class="tang"]//text()') #获取div下的所有内容
- #取属性
- r7=tree.xpath('//div[class="song"]/img/@src')
获取二手房的所有标题
- from lxml import etree
- import requests
- #爬取页面源码数据
- url='https://bj.58.com/ershoufang/'
- headers={'User-Agent':
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- page_text = requests.get(url = url,headers = headers).text
- tree = etree.HTML(page_text)
- li_List = tree.xpath('//section[@class = "list"]/div')
- fp = open('58.txt','w',encoding='utf-8')
- for li in li_List:
- title = li.xpath('./a/div[2]//div/h3/text()')[0]
- print(title)
- fp.write(title+'\n')
- import os.path
- from lxml import etree
- import requests
- #爬取页面源码数据
- url='http://pic.netbian.com/'
- headers={'User-Agent':
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0'}
- page_text = requests.get(url = url,headers = headers).text
- tree = etree.HTML(page_text) #实例化一个etree对象
- li_List = tree.xpath('//div[@class="slist"]/ul/li')
- #创建一个文件夹
- if not os.path.exists('./pictures'):
- os.mkdir('./pictures')
- #遍历标签li里面的内容
- for li in li_List:
- img_src='http://pic.netbian.com'+li.xpath('./a/span/img/@src')[0]
- img_name=li.xpath('./a/span/img/@alt')[0]+'.jpg'
- #通用处理中文乱码的解决方案
- img_name=img_name.encode('iso-8859-1').decode('gbk')
- #print(img_name,img_src)
- #请求图片进行持久化存储
- img_data=requests.get(url=img_src,headers=headers).content
- img_path='./pictures'+img_name
- with open(img_path,'wb') as fp:
- fp.write(img_data)
- print('下载成功')
-
反爬机制:利用验证码。
识别验证码图片中的数据,用于模拟登陆操作。
识别验证码的操作:1.人工肉眼识别(不推荐) 2.第三方自动识别
举例:古诗文网验证码识别
使用打码平台识别验证码的编码流程:
1.将验证码图片进行本地下载
2.调用平台提供的示例代码进行图片数据识别
异步爬虫的目的:在爬虫中使用异步实现高性能的数据爬取操作。
异步爬虫的方式:
1.多线程,多进程(不建议):
好处:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作就可以异步执行。
弊端:无法无限制的开启多线程或者多进程。
2.线程池、进程池(适当使用):
好处:可以降低系统对进程或者线程创建和销毁的一个频率,从而很好的降低系统开销。
弊端:池中线程或进程的数量是有上限的。
(使用原则:线程池处理的是阻塞且耗时的操作)
3.单线程+异步协程 (推荐):
event_loop:事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件时,函数就会被循环执行。
coroutine:协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用 async 关键字来定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象。
task:任务,它是对协程对象的进一步封装,包含了任务的各个状态。
future:代表将来执行或还没有执行的任务,实际上和 task 没有本质区别。
async:定义一个协程。
await:用来挂起阻塞方法的执行。
单线程串行:
- import time
- #使用单线程串行方式执行
- def get_page(str):
- print('正在下载:',str)
- time.sleep(2)
- print('下载成功:',str)
- name_list=['qe','eefew','ddds']
- start_time=time.time()
- for i in range(len(name_list)):
- get_page(name_list)
-
- end_time=time.time()
- print('%d,second'% (end_time,start_time))
线程池方式执行:
- import time
- #导入线程池模块对应的类
- for multiprocessing.dummy import Pool
- #使用线程池方式执行
- start_time = time.time()
- def get_page(str):
- print('正在下载:',str)
- time.sleep(2)
- print('下载成功:',str)
- name_list=['qe','eefew','ddds']
- #实例化一个线程池对象
- pool=Pool(4)
- #将列表中每个元素传递给get_page进行处理
- pool.map(get_page,name_list)
- end_time=time.time()
- print(end_time-start_time)
协程:
- import asyncio
- async def request(url):
- print('正在请求的url是',url)
- print('请求成功',url)
- return url
- #async修饰的函数,调用之后返回的一个协程对象
- c=request('www.baidu.com')
-
- #创建一个事件循环对象
- #loop=asyncio.get_event_loop()
-
- #将协程对象注册到loop中,然后启动loop
- #loop.run_until_complete(c)
-
- #task的使用
- #loop=asyncio.get_event_loop()
- #基于loop创建了一个task对象
- #task=loop.create_task(c)
- #print(task)
-
- #loop.run_until_complete(task)
- #print(task)
-
- #future的使用
- # loop=asyncio.get_event_loop()
- # task=asyncio.ensure_future(c)
- # print(task)
- # loop.run_until_complete(task)
- # print(task)
-
- def callback_func(task):
- #result返回的就是任务对象中封装的协程对象对应函数的返回值
- print(task.result())
- #绑定回调
- loop=asyncio.get_event_loop()
- task=asyncio.ensure_future(c)
- #将回调函数绑定到任务对象中
- task.add_done_callback(callback_func())
- loop.run_until_complete(task)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。