初识爬虫
爬虫的概念
「什么是爬虫」
爬虫
:通过编写程序,模拟浏览器上网,并抓取有价值的数据的过程
反爬虫
:门户网站通过制定相应的策略或技术手段,来阻止爬虫程序对其网站数据的爬取
反反爬
:爬虫程序可以采用一些技术手段,来绕过或破坏门户网站的反爬机制,从而爬取到有用的数据
❝
爬虫与反爬虫就是一对矛与盾
❞
「爬虫合法性探究」
爬虫可能带来的风险?
-
爬虫干扰了被访问网站的正常运营
-
爬虫抓取了受到法律保护的特定类型的数据或信息
如何合理地使用爬虫?
-
对爬虫程序进行优化,避免干扰网站的正常运行
-
不要爬取涉及商业机密等敏感信息
「爬虫的君子协议」
通常,网站的robots.txt
文件中声明了那些数据可以被爬取,那些数据不可以被爬取(非强制性)
爬虫的分类
在不同的使用场景下,爬虫的分类有
-
通用爬虫
:抓取一整张页面的数据(很可能包含大量无用信息) -
聚焦爬虫
:抓取页面中特定的局部内容,必须建立在通用爬虫的基础之上 -
增量爬虫
:只会爬取网站中最新更新的数据
网络请求与响应
http协议
http(s)协议是服务器和客户端进行数据交互的一种形式,服务器和客户端都需要遵守该协议才能进行数据交互
https协议是http协议的升级版,服务器与客户端的数据交互是通过证书加密的,攻击者很难获得有价值的信息
「常用的请求头信息」
Request Header | 描述 |
---|---|
User-Agent | 请求载体的身份标识 |
Connection | 请求完毕后,保持连接还是断开连接 |
「常用的响应头信息」
Response Header | 描述 |
---|---|
Content-Type | 服务器响应数据的类型 |
requests模块
requests
是python中的一个基于网络请求的模块,用来模拟浏览器发送请求。
「requests模块的安装与使用」
pip install requests
import requests
url = 'http://www.baidu.com'
resp = requests.get(url) #发起一个get请求,并获得响应数据
page_content = resp.text
print(page_content)
属性 | 描述 |
---|---|
resp.text | 以字符串形式返回,通常是页面的html源代码 |
resp.content | 以二进制形式返回,比如一张图片、一个音频 |
resp.json() | 返回一个字典对象(当响应数据是json类型时使用) |
「基于requests的简易网页采集器(使用到了UA伪装)」
import requests
# 1. 准备数据
url = 'https://www.sogou.com/web'
word = input('Enter a word:')
params = { #请求参数,拼接在url后
'query': word }
headers = { #请求头,伪装成浏览器
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36' }
# 2. 发起请求
resp = requests.get(url,params,headers=headers)
resp.encoding = resp.apparent_encoding
page_content = resp.text
print(page_content)
# 3. 持久化存储
file_name = word + '.html'
with open(file_name,'w',encoding='utf-8') as fp:
fp.write(page_content)
print(file_name,'保存成功!')
数据解析
数据解析
是在得到整个网页源代码后,对其中的有用信息进行提取的过程。属于聚焦爬虫
「数据解析的一般步骤」
检查网页源代码发现,有价值的数据一般存放在标签中,或者标签的属性中。所以数据解析的一般步骤是:1.获取网页源代码 2.标签定位 3.解析数据
❝
F12检查元素中的数据不一定在页面源代码中,也有可能是通过ajax动态刷新的数据,这是我们在数据解析时需要注意的。数据解析要以页面源代码为准!
❞
「Python中数据解析的三种方式」
1.正则表达式(通用) 2.BeautifulSoup4(python独有) 3.xpath(推荐,通用性最强)
使用正则表达式
建议先把要提取的那部分源码单独复制,对照着去写正则表达式(F12太乱了🤣)
从重复的标签(如li)开始写正则,那么这个正则可以提取到多组数据哦
「re.findall(pattern,string,flags)」
参数 | 描述 |
---|---|
pattern | 匹配的正则表达式 |
string | 待匹配的文本字符串 |
flags | 标志位。用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等 |
标志位 | 描述 |
---|---|
re.S | 使.能够匹配包括换行在内的所有字符 |
re.M | 多行匹配,影响^和$ |
re.I | 使匹配对大小写不敏感 |
正则表达式 | 描述 |
---|---|
. | 匹配除换行符外的任意单个字符 |
* | 匹配多个字符 |
? | 非贪婪匹配 |
使用BeautifulSoup
对于一个网页来说,都有一定的特殊结构和层级关系,而且很多节点都用id和class来区分。所以可以借助网页的结构和属性来提取数据。
「使用BeautifulSoup的一般步骤」
-
实例化一个BeautifulSoup对象,并且将页面源代码加载到该对象中
-
调用BeautifulSoup对象提供的属性或方法进行标签定位和数据提取
「BeautifulSoup4的安装与使用(需要一并下载lxml解析器)」
pip install bs4
pip install lxml
from bs4 import BeautifulSoup
import requests
# 加载html有两种方式
# 方式一 使用本地html文档中的数据
fp = open('./猫羽雫.html','r',encoding='utf-8')
soup = BeautifulSoup(fp,'lxml')
print(soup)
#我们发现,soup对象的内容就是加载到该对象中的html源码
# 方式二 从互联网上获取html源码
url = 'https://www.baidu.com'
resp = requests.get(url)
resp.encoding = resp.apparent_encoding
soup = BeautifulSoup(resp.text,'lxml')
print(soup)
「BeautifulSoup对象中提供的属性和方法」
- 根据标签名或选择器定位,返回标签之间的所有内容
方法 | 描述 |
---|---|
find(tag,attr=value) | 根据标签名和属性进行定位,只返回符合条件的第一个元素内容 |
find_all(tag,attr=value) | 返回一个列表,用法同上 |
select(css选择器) | 使用CSS选择器进行定位,返回一个列表 |
2. 获取标签之间的文本数据(不包括子标签)
属性 | 描述 |
---|---|
.text | 获取所有文本内容 |
.string | 只能获取直系的文本内容 |
3. 获取标签中指定属性的值
属性 | 描述 |
---|---|
[attr] | 获取属性对应的属性值 |
使用xpath解析
xpath解析是最常用、最便捷、最高效,且通用性最强的解析方式。xpath是根据元素所处层级进行定位的
「xpath解析的一般步骤」
-
实例化一个etree对象,并将要解析的html页面源码数据加载到对象中
-
调用etree对象的
xpath(xpath表达式)
方法结合xpath表达式实现标签的定位和数据提取
「xpath的安装与使用」
pip install lxml
from lxml import etree
import requests
# 加载html有两种方式
# 方式一 使用本地html文档中的数据
etree.parse('./猫羽雫.html')
# 方式二 从互联网上获取html源码
url = 'https://www.baidu.com'
resp = requests.get(url)
resp.encoding = resp.apparent_encoding
etree.HTML(page_content)
方法 | 描述 |
---|---|
etree.HTML(html) | 实例化一个etree对象,并加载要解析的html |
etree.parse(filepath) | 实例化一个etree对象,并加载要解析的html(本地html) |
xpath(xpath表达式) | 使用xpath表达式进行标签定位,返回一个列表 |
xpath()方法返回一个列表,如果xpath表达式只进行了定位,没有进行数据提取,那么列表中每个元素都将是一个Element对象。
「xpath表达式用法」
- 根据html元素层级进行定位
xpath表达式 | 描述 |
---|---|
/ | 表示单个层级,放在开头表示html根元素 |
// | 表示多个层级(常用) |
./ | 表示当前标签 |
[@attr=value] | 根据属性进行定位 |
[index] | 根据元素所处位置进行定位,index从1开始 |
- 提取标签之间的文本数据
xpath表达式 | 描述 |
---|---|
/text() | 获取标签中的文本,返回一个列表 |
//text() | 可以获取标签中非直系的文本,返回一个列表 |
- 提取标签中指定属性的值
xpath表达式 | 描述 |
---|---|
/@attr | 获取标签中属性对应的值 |
数据解析实战
我们将通过一个案例具体说明
Wallhaven网站
需求:获取给定链接下的图片并下载保存到本地
import requests
import time
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.88 Safari/537.36'
}
out_path = 'D:\\wallhaven' # 下载图片保存目录,可自定义
start = 1 # 给定爬取起始页
def get_img():
api_url = f'https://wallhaven.cc/toplist?page={start}'
data = requests.get(api_url)
data = data.text
# 解析数据
soup = BeautifulSoup(data, "html.parser")
item_list = soup.find_all("a", class_='preview')
# 通过F12分析图片位置,找到a标签并且class为'preview'的数据
for lst in item_list:
url = lst.get('href') # 从数据列表中获取属性href值 类似https://wallhaven.cc/w/we5ov6
# 继续从上个url中获取图片数据并解析
data1 = requests.get(url).text
soup = BeautifulSoup(data1, "html.parser")
its = soup.find_all('img', id='wallpaper') # 获取源图片地址,大图,即为所需图片
for wall in its:
url2 = wall.get('src')
res = requests.get(url=url2, headers=headers).content
name = url2.split('/')[-1]
img_save_path = out_path + '\\' + name
with open(img_save_path, 'wb') as f: # 存储图片
f.write(res) # 写入图片
print(name + ' >>>>>>>>>>>>>>>>>>>> 下载完成')
f.close()
# 循环下载主入口,粗略
while 1:
get_img()
time.sleep(5)
start += 1
# 可自定义循环退出条件
if start > 500:
break
写在后面,爬取了一些二次元图如下:
看多了也就那样~~~