爬虫


crawer_process

[toc]

从根页面获取链接

打开源代码,Ctrl U观察源代码中是否有要获取的内容

有直接爬取

获取源代码

再看一看获得源代码里有无要获取的内容,有说明爬取没问题

找到要获取元素的上上上级标签,最好是<li>,代码中空格用.*?来代替,中间一帮子不知道啥玩意也用这个代替

数据获取

B/S三层架构

浏览器访问网页,是先向目标Web服务器发送HTTP请求,然后接收并解析服务器返回的数据。

因此想要爬取网页中的数据,首先得先构造HTTP请求并发送给Web服务器,并接收返回响应数据。一般使用requests模块处理HTTP的各种请求。

发送请求

GET 和 POST 是 HTTP 协议中最核心的两种请求方法,GET 请求可以通过字符串在URL中体现,POST 请求的具体数据隐藏在请求体中。

response = requests.get(url, headers=header)  # 发送请求
if response.status_code == 200:  # 判断是否返回正常响应
    print("请求成功")
else:
    print("请求失败,状态码:", response.status_code)
response.close()  # 访问完随时关闭请求

GET 请求

response = requests.get(url)
  • url:请求地址
  • headers:请求头,字典格式
  • params:参数,格式是字典

POST 请求

response = requests.post(url, data)
  • data:以POST请求发送数据,格式是字典

请求返回的是Response 对象。

Response 对象

Response 对象即服务器对客户端请求的封装响应,其包含了服务器返回给客户端的所有信息,包括响应体、响应头等。

响应体

在浏览器与服务器交互时,服务器返回的响应通常由响应头和响应体组成,响应体就是服务器返回的实际数据。

函数 含义
response.text 以字符串形式返回服务器响应
response.content 以字节形式返回服务器响应<br / >(可以获取网页中的图片、文件等二进制数据)
response.json() 将响应内容解析为JSON格式

响应头

在HTTP协议中,响应头包含了关于报文的元数据,尤其是可以通过状态码判断是否响应成功。

函数 含义
response.status_code 状态码
response.request.headers 请求报文头部
response.headers 响应报文头部
response.request._cookies 请求报文 Cookie
response.cookies 响应报文 Cookie

数据解析

通过requests模块获得的网页请求,包含了整个网页文件的内容。纷繁复杂,啥都有。因此需要对网页文件进行解析,获取想要的内容后再进行进一步的处理。

正则表达式

详见《正则表达式》

Python中的正则

Python中使用re模块撰写正则表达式。

方法名 语法 含义
findall re.findall(r'正则', '字符串') 匹配字符串所有内容,以表格格式返回
finiter re.finditer(r'正则', '字符串') 匹配字符串所有内容,以迭代器格式返回更高效
.group()函数获取迭代内容的具体数据
search re.search(r'正则', '字符串') 匹配字符串的首个匹配内容,以match对象返回
.group()获取数据
没有返回 None
match re.match(r'正则', '字符串') 默认字符串开头就要匹配
哪家好人用这个
compile re.compile(r'正则') 预加载表达式,将正则表达式封装成一个对象,查询时可以直接调用

爬虫时使用正则

obj = re.compile(r"<div class='.*?'>(?P<分组名>.*?)</div>", re.S)  # 撰写正则规则
# re.S 让.能匹配换行符
# (?P<分组名>正则) 可以从正则匹配内容中进一步提取内容
result = obj.finditer(s)  # 匹配所有结果
for it in result:
    it.group("id")  # 获取组里的内容

BeautifulSoup

BS4就是根据标签的属性去查找,和CSS查找器差不多。

首先要将页面源代码生成 bs 对象。

page = bs4.BeautifulSoup(resp.text, "html.parser")

从 bs 对象中查找数据

语法 含义
page.find("标签名", 属性="值") 找首个属性值的标签
如果属性是关键字,就用 属性_
page.find("标签名", attrs={"属性": "值"}) 找首个属性值的标签
page.find_all("标签名", 属性=值) 找到所有标签
以列表形式给出

通过BS4拿到标签后,可以用.text表示标签中内容,通过.get("属性")获取标签的属性值。

XPath

XPath是一种用于在 XML 或 HTML 文档中定位和选择节点的查询语言,其提供了非常简洁明了的路径选择表达式,另外它还提供了超过 100 个内建函数用于字符串、数值、时间的匹配以及节点、序列的处理等等,几乎所有我们想要定位的节点都可以用XPath来选择。

表达式 描述
nodename 选取节点所有子节点
/ 根节点
// 从匹配选择的当前节点
. 当前节点
.. 当前节点父节点
@属性名 选取属性
text() 获取文本
表达式 描述
/标签1/标签2[n] 标签1子元素中第 n 个标签2
/标签1/标签2[last()-(n-1)] 标签1子元素中倒数第 n 个标签2
/标签1/标签2[position() < n] 标签1子元素中前 n 个标签2
//标签1[@属性1] 选取拥有属性1的全部标签1
//标签1[@属性1=x] 选取拥有属性1x的全部标签1
* 通配符
from lxml import etree

page = response.content.decode()
# 将HTML内容转换为xml文档对象
html = etree.HTML()
# 也可以直接读取 html 文件
html = etree.parse("文件路径.html")
title = html.xpath("表达式")
  1. 定位到包含数据所有元素
  2. 找到包含具体内容的子元素
  3. 对子元素遍历,提取详细内容

反爬

请求头

  • User-Agent
  • 防盗链接——检测请求来源(请求头中Refer属性,即打开请求前在哪个页面)

Max retries exceeded with url

request.get(url, verify=False) 去掉安全验证

编码不一样

request编码默认UTF-8

<meta>中获取编码

resp.encoding = ‘’

模拟浏览器登陆

通过代码模拟人工请求登录,然后访问数据

  1. 通过抓包,找到登录接口

    • 打开目标网页,点击登录

    • 打开F12,随便输入账号密码

    • 查看控制台登录请求中的url地址和请求参数

  2. 分辨鉴权方式

    • cookie + session
    • token
  3. 编码传入正确账号和密码

使用 cookie + session 模拟登陆

  1. 传账号密码登录
  2. 登录后保存cookie(返回报文头的 set-cookie)
  3. 请求其他页面时携带cookie
# 1
login_url = "..."
params = {
    "username": "...",
    "password": "..."
}
response = requests.post(url,headers , data=params)

# 2 获取cookies
cookies = response.cookies
# 2.1
request.post(url, cookies=cookies)
#2.2
headers = {
    'Cookie': "..."
}
# 2.3 自动保留cookie,最常用
http = requests.session() # 创建请求对象
response = http.post(url, data)

使用 token 模拟登录

  1. 传递账号密码登录
  2. 登录后保存token(响应报文响应体
  3. 请求其他页面携带token

鉴权

cookie

session

token

防盗链

很多页面内容不是直接显示在html中的,是后续ajax异步请求拿到的

  1. 在 F12 网络里找到对应cont

  2. 根据F12返回内容拿到url

  3. 对url进行修改

  4. 下载


refer

代理——

防止封 IP

通过第三方机器发送请求

proxies = {
    "https": "https://xx.xx.xx.xx:xxxx"
}

request.get(url, proxies)

Cookie池

多线程

async def download(url):
    async def download(url):
    # 发送请求
    # 得到文件内容
    # 保存到文件
    async with aiohttp.ClientSession() as session: # request
        async with session.get(url) as resp: resp = request.get()
            with open(name, mode='wb') as f:
                f.write(await resp.content.read())
    
async def main():
    urls = []
    tasks = []
    
    for url in urls:
        d = download(url)
        tasks.append(asyncio.create_task(d))
      请求
    await asyncio.wait(tasks)
    
if __name__ == '__main__':
    asyncio.run(main())

多线程

多进程

协程

爬视频

M3U8

  1. 找到m3u8
  2. 通过m3u8下载ts文件
  3. 合并成mp4

Selenium

模仿人自动化操作页面

  1. 打开浏览器

    driver = selenium.webdriver.Chrome()
  2. 操作页面

    driver.get(url)
  3. 获取页面数据

    html =  driver.page_source
  • current_url:当前 url
  • title:页面标题
  • page_source:页面 html 代码
  • window_handler:获取浏览器所有窗口句柄
  • current_window_handle:获取当前窗口句柄

元素定位

通过 id 查找

element = driver.find_element_by_id("id值")

**通过 name 定位 **

element = driver.find_element_by_name("")

**通过 class 定位 **

driver.find_element_by_class_name("").

通过xpath定位

ele = driver.find_element(By.XPATH, 'xPath值')

元素属性

  • tagname:标签名
  • text:标签文本
  • parent:父标签
  • get_attribute():获取属性
  • s_displayed():判断元素是否可见
  • click():点击元素
  • send_keys():输入内容
  • clear():清空表单

隐氏等待

页面加载出来就会继续执行,否则

driver.implicitly_wait(值);


嵌套网页

<iframe>

  1. 切换到指定 iframe

    # 1
    driver.switch_to.frame()

    值可以是 iframe 的name属性,也可以是通过xpath查找的对象

  2. 切换回默认页面

    driver.switch_to.default_content()
  3. 切换到父级iframe

    driver.switch_to_parent_frame()

鼠标操作

from selenium webdriver import ActionChains

ac = ActionChains(driver)
  • click:鼠标左击
  • double_click:双击
  • context_click:右击
  • move_to_element:移动到某个节点
  • click_and_hold:按住左键
  • move_by_offse:相对当前位置进行移动
  • drag_and_drop:在一个位置按下,到另一个位置释放
  • release():释放鼠标
  • perform():执行上述操作

反Selenium

  • User-Agent
  • 请求头有自动化标识
  • window对象会有特定属性
  • 页面加载行为
  • --disable-infobars:禁止Chrome显示自动化测试通知栏
  • excludeSwitchesenable-automation:排除启动自动化程序开关
  • useAutomationExtension: False“禁用自动化扩展
  • Page.addScriptToEvaluateOnNewDocument:在每次页面加载执行JS代码,隐藏生成的属性


# 添加参数
opt = webdriver.ChromeOptions()
opt.add_argument('--disableinfobars')
opt.add_experimental_option("excludeSwitches", ["enable-automation"])
opt.add_experimental_option('useAutomationExtension', False)

driver = selenium.webdriver.Chrome(option=opt)

# 在打开页面前,运行JS
with open('hide.js') as f:
    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {"source": f.read()})

找加密

查找到输入参数后调用参数生成的函数

输入不同参数看看生成出的不同结果

调试到函数

反调试

  • 禁用F12

    先开F12,再进页面

  • 检测调试工具

      
  • 定时检测

    function Debugging() {
        debugger
        setInterval(Debugging, 1000)
    }
  • 清空控制台

    function startClearConsole() {
        setInterval(function () {
            console.clear()l;
        }, 1000);
    }
var debugflag = false;

  endebug(false, function () {
      document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!');
      document.write("<br/>");
      document.write("Welcome for People, Not Welcome for Machine!");
      debugflag = true;
  });
  txsdefwsw();
  document.onkeydown = function() {
    if ((e.ctrlKey) && (e.keyCode == 83)) {
      alert("检测到非法调试,CTRL + S被管理员禁用");
      return false;
    }
  }
  document.onkeydown = function() {
    var e = window.event || arguments[0];
    if (e.keyCode == 123) {
      alert("检测到非法调试,F12被管理员禁用");
      return false;
    }
  }
  document.oncontextmenu = function() {
    alert('检测到非法调试,右键被管理员禁用');
    return false;
  }
	$(function()
	{
		if (!debugflag && !window.navigator.webdriver) {
      loadTab();
    }
		if(!isSupportCanvas())
		{
			$("#browertip").show();
		}
	});

	function isSupportCanvas()
	{
	   var elem = document.createElement('canvas');
	   return !!(elem.getContext && elem.getContext('2d'));
	}
// 检测调试器是否开始
function endebug(off, code) {
    if (!off) {
        !function(e) {
            function n(e) {
                function n() {
                    return u
                }
                function o() {
                    window.Firebug && window.Firebug.chrome && window.Firebug.chrome.isInitialized ? t("on") : (a = "off",
                    console.log(d),
                    console.clear(),
                    t(a))
                }
                function t(e) {
                    u !== e && (u = e,
                    "function" == typeof c.onchange && c.onchange(e))
                }
                function r() {
                    l || (l = !0,
                    window.removeEventListener("resize", o),
                    clearInterval(f))
                }
                "function" == typeof e && (e = {
                    onchange: e
                });
                var i = (e = e || {}).delay || 500
                  , c = {};
                c.onchange = e.onchange;
                var a, d = new Image;
                d.__defineGetter__("id", function() {
                    a = "on"
                });
                var u = "unknown";
                c.getStatus = n;
                var f = setInterval(o, i);
                window.addEventListener("resize", o);
                var l;
                return c.free = r,
                c
            }
            var o = o || {};
            o.create = n,
            "function" == typeof define ? (define.amd || define.cmd) && define(function() {
                return o
            }) : "undefined" != typeof module && module.exports ? module.exports = o : window.jdetects = o
        }(),
        jdetects.create(function(e) {
            var a = 0;
            var n = setInterval(function() {
                if ("on" == e) {
                    setTimeout(function() {
                        if (a == 0) {
                            a = 1;
                            setTimeout(code)
                        }
                    }, 200)
                }
            }, 100)
        })
    }
}

写油猴脚本新函数覆盖掉?


图形验证码

import ddddocr
ocr = ddddocr.DdddOcr()
code = ocr.classification(img)

字体反爬

自己整了个字体,导致F12乱码

还是用OCR


文章作者: Jarrycow
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Jarrycow !
评论
  目录