freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Python-Spider 爬虫基础
2025-03-03 10:42:00
所属地 四川省

爬虫的基本流程

  • 基本流程

    • 目标数据:需要什么数据

    • 来源地址

    • 结构分析

      • 具体数据在哪(网站、还是APP)

      • 如何展示的数据

    • 实现构思

    • 编写代码

爬虫的基本手段

  • 破解请求限制

    • 请求头设置,如:useragant为有效客户端

    • 控制请求频率(根据实际情景)

    • IP代理

    • 签名/加密参数从html/cookie/js分析

  • 破解登录授权

    • 请求带上用户cookie信息

  • 破解验证码

    • 简单的验证码可以使用识图读验证码第三方库

解析数据

  • HTML Dom解析

    • 正则匹配,通过的正则表达式来匹配想要爬取的数据,如:有些数据不是在html 标签里,而是在html的script 标签的js变量中

    • 使用第三方库解析html dom,比较喜欢类jquery的库

  • 数据字符串

    • 正则匹配(根据情景使用)

    • 转 JSON/XML 对象进行解析

简单爬取页面数据

from urllib.request import urlopen

url = 'http://www.baidu.com'
resp = urlopen(url)
print(resp.read().decode()[:100])

urllib

  • requset.urlopen(url,data,timeout)

    • 第一个参数url即为URL,是必须要传送的。第二个参数data是访问URL时要传送的数据,第三个timeout是设置超时时间。

    • 第二三个参数是可以不传送的,data默认为空None,timeout默认为`socket._GLOBAL_DEFAULT_TIMEOUT

  • response.read()

    • read()方法就是读取文件里的全部内容,返回bytes类型

  • response.getcode()

    • 返回 HTTP的响应码,成功返回200,4服务器页面出错,5服务器问题

  • response.geturl()

    • 返回 返回实际数据的实际URL,防止重定向问题

  • response.info()

    • 返回 服务器响应的HTTP报头

获取响应信息

代码含义
resp.json()获取响应内容(以json字符串)
resp.text获取响应内容 (以字符串)
resp.content获取响应内容(以字节的方式)
resp.encoding获取网页编码
resp.headers获取响应头内容
resp.request.headers请求头内容
resp.url获取访问地址
resp.cookie获取cookie

request对象

使用urllib.request.urlopen发送请求时,可以将参数封装到一个Request对象中。
参数包含:

  • url 发送的请求链接

  • headers 请求头信息

  • data 请求数据

请求头

名称含义
Accept告诉服务器,客户端支持的数据类型
Accept-Charset告诉服务器,客户端采用的编码
Accept-Encoding告诉服务器,客户机支持的数据压缩格式
Accept-Language告诉服务器,客户机的语言环境
Host客户机通过这个头告诉服务器,想访问的主机名
If-Modified-Since客户机通过这个头告诉服务器,资源的缓存时间
Referer客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的。(一般用于防盗链)
User-Agent客户机通过这个头告诉服务器,客户机的软件环境
Cookie客户机通过这个头告诉服务器,可以向服务器带数据
Refresh服务器通过这个头,告诉浏览器隔多长时间刷新一次
Content-Type服务器通过这个头,回送数据的类型
Content-Language服务器通过这个头,告诉服务器的语言环境
Server服务器通过这个头,告诉浏览器服务器的类型
Content-Encoding服务器通过这个头,告诉浏览器数据采用的压缩格式
Content-Length服务器通过这个头,告诉浏览器回送数据的长度

基本请求

HTTP的基本请求

  • get

  • post

  • put

  • delete

  • head

  • options

GET请求

大部分被传输到浏览器的html,images,js,css, … 都是通过GET方法发出请求的。它是获取数据的主要方法
Get请求的参数都是在Url中体现的,如果有中文,需要转码,这时我们可使用

  • urllib.parse.urlencode() 转换键值对

  • urllib.parse. quote() 转换一个值

POST请求

POST请求的参数需要放到Request请求对象中,data是一个字典,里面要匹配键值对

# 当data为字典,要转换为bytes类型
from urllib.parse import urlencode
_data = {}
data = urlencode(_data).encode

post发送表单数据和json数据皆可

import requests

url = url

# from表单数据
from_data = {
    "name": "micdy",
    "book": "test"
}

req = requests.post(url, data=from_data)
print(req.text)

# json数据
json_data = {
    "name": "micdy",
    "book": "test"
}

req = requests.post(url, json=json_data)
print(req.text)

Cookie

urlib使用cookie

使用cookie保持登录,为了保持Cookie不丢失可以urllib.request.HTTPCookieProcessor来扩展opener的功能

handler = HTTPCookieProcessor()
opener = build_opener(handler)

保存cookie
可以利用本模块的http.cookiejar.CookieJar类的对象来捕获cookie并在后续连接请求时重新发送,比如可以实现模拟登录功能。该模块主要的对象有CookieJar、FileCookieJar、MozillaCookieJar、LWPCookieJar

cookie_jar = MozillaCookieJar()
handler = HTTPCookieProcessor(cookie_jar)
cookie_jar.save('cookie.txt',ignore_discard=True,ignore_expires=True)

使用保存的cookie

cookie_jar.load('cookie.txt',ignore_discard=True,ignore_expires=True)
handler = HTTPCookieProcessor(cookie_jar)
opener = build_opener(handler)

requests使用cookie

使用Session方法启用会话

s = requests.Session()
print(s.cookies)

'''
-> Cookie session=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTczNzYxOTMxNn0.EbyLG-885Ng3sAIr7NNxZS0NnufKSe6jm76rzVAa7r8
'''

requests.Session()会创建一个会话对象,这个对象会在多次请求间自动管理 cookie。当你使用这个会话对象发送请求时,服务器返回的 Set - Cookie头信息会被会话对象自动保存。在后续使用该会话对象发送的请求中,会话对象会自动在请求头里添加保存的 cookie,从而保持会话状态。

  • 创建会话对象:s = requests.Session()创建了一个 Session对象 s

  • 登录请求:login_resp = s.post(url, data=form_data)发送登录请求,服务器返回的 cookie会被 s自动保存。

  • 后续请求:info_resp = s.get(url)发送后续请求时,s会自动在请求头中添加之前保存的 cookie,以此保持会话状态。

import requests

def login():
    url = "http://localhost:8000/playground/login"
    data = {
        "uname": "admin",
        "password": "123456"
    }
    s = requests.Session()
    login_resp = s.post(url, data=data)

    url = "http://localhost:8000/playground/user_info"
    login_resp = s.get(url)
    print(login_resp.text)
  
if __name__ == "__main__":
    login()

Token

Token是服务器生成的一串字符串,作为客户端在后续请求时的身份标识和授权依据。客户端成功登录或完成特定操作后,服务器会创建一个Token并返回给客户端,之后客户端每次向服务器发送请求时都需要携带这个Token,服务器通过验证Token的有效性来确认客户端的身份和权限。
服务器接收到登录信息后,对用户的身份进行验证,若验证通过,服务器会根据用户的相关信息(如用户 ID、角色等)生成一个Token。

Token具有时效性。

Token通常由三部分组成

  • 头部(Header):包含Token的类型(如JWT)和使用的签名算法(如 HMAC SHA256、RSA 等),并进行Base64Url编码。

  • 载荷(payload):包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明,可分为注册声明(如iss发行人、sub主题、aud受众等)、公开声明和私有声明。同样进行 Base64Url 编码。

  • 签名(Signature):为了创建签名部分,需要使用编码后的头部、编码后的载荷、一个秘钥(只有服务器知道),使用头部中指定的签名算法进行签名,以确保Token未被篡改。

服务器将生成的Token返回给客户端,客户端通常会将Token存储在本地,如浏览器的本地存储(Local Storage)或会话存储(Session Storage)中,也可以存储在Cookie里。
后续的请求中,可以在Authorization字段携带Token,服务器会对Token进行验证,

利用Token登录

import requests

url = "http://localhost:8000/playground/login2"
data = {
    "uname": "admin",
    "password": "123456"
}
resp = requests.post(url, json=data)
token = resp.json().get("access_token")
login_url = "http://localhost:8000/playground/user_info2"

headers = {
    "Authorization": f"Bearer {token}"
}
resp = requests.get(login_url, headers=headers)
print(resp.json())

使用场景

  • 身份验证:用于验证用户的身份,确保只有经过授权的用户才能访问受保护的资源。例如,用户登录电商网站后,服务器生成 Token,用户在浏览个人订单、修改个人信息等操作时都需要携带该 Token 进行身份验证。

  • 授权:可以根据 Token 中包含的用户角色和权限信息,决定用户可以执行哪些操作。比如,管理员用户的 Token 可能包含更高的权限,允许其进行用户管理、商品上架等操作,而普通用户的 Token 权限则相对较低。

  • 单点登录(SSO):在多个相关的 Web 应用中,用户只需在一个应用中登录,服务器生成的 Token 可以在其他关联应用中使用,实现一次登录,多处访问。

urllib底层

当你获取一个URL你使用一个opener(一个urllib.OpenerDirector的实例)。在前面,我们都是使用的默认的opener,也就是urlopen。它是一个特殊的opener,可以理解成opener的一个特殊实例,传入的参数仅仅是url,data,timeout
如果我们需要用到Cookie,只用这个opener是不能达到目的的,所以我们需要创建更一般的opener来实现对Cookie的设置

网页页面

  • 静态页面:访问有UI页面URL,可以直接获取数据

  • 动态页面(AJAX):访问有UI页面URL,不能获取数据。需要抓取新的请求获取数据

有些网页内容使用AJAX加载,而AJAX一般返回的是JSON,直接对AJAX地址进行post或get,就能返回JSON数据了。在xhr,异步请求中寻找。

SSL证书

如果SSL证书验证不通过,或者操作系统不信任服务器的安全证书,会警告用户证书不受信任。
忽略ssl的安全性

import ssl
context = ssl._creat_unverified_context()
response = urlopen(request,context = context)

伪装爬虫请求

有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应。

fake-useragent

pip install fake-useragent

fake-useragent在创建对象时,可能创建不了,多部分原因为服务器访问不到的原因
可以拷贝fake-useragent_version.json的配置文件到用户目录

from fake_useragent import UserAgent

ua = UserAgent()

print(ua.chrome)
print(ua.firefox)
print(ua.edge)
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Mobile Safari/537.36
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:123.0) Gecko/20100101 Firefox/123.0
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0

代理ip

代理分类

  • 透明代理:目标网站知道你使用了代理并且知道你的源IP地址,这种代理显然不符合我们这里使用代理的初衷

  • 匿名代理:匿名程度比较低,也就是网站知道你使用了代理,但是并不知道你的源IP地址

  • 高匿代理:这是最保险的方式,目标网站既不知道你使用的代理更不知道你的源IP


代理网站

代理网站目前可用,但不一定稳定。毕竟这个产业目前情况就是这样。

构建hander实现

hander = ProxyHandler({'http': 'ip:port'})
opener = build_opener(hander)
# 爬虫 # Python爬虫
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者
文章目录