Python 进行网络操作、爬虫相关的实践。如:
socket:套接字的相关,实现不同主机的进程间的数据交换。requests:基于 urllib 的 Python 第三方库,但比 urllib 更加方便易用。lxml:数据解析,xpath 解析器解析 html,bs4 的罗嗦与不好维护,lxml 具有基于 C 的超越 bs4 的速度- BeautirulSoup:BS4:优雅地处理 html,but 打不过 lxml,OUT
scrapy:一套成熟的 Python 爬虫框架,抓取网站数据并提取结构化数据。selenium:模拟浏览器的数据采集,web 应用程序自动化测试框架,主要用于 web 应用程序的测试。模拟真实用户对浏览器进行操作,适用于 js 渲染多的you-get:一个 YouTube/Youku/Niconico 视频下载器,使用 Python3 编写。pytesser:验证码破解newscatcher:获取新闻资讯的工具包,它时刻监控者成千上万个新闻媒体并对其进行聚合,它包含丰富的 API 接口,开发者可以通过时间、新闻源、关键字等方式来获取新闻资讯。aiohttp:基于 asyncio 的异步 HTTP 网络库。
概述
常识
爬虫:Spider:网络蜘蛛,自动化批量采集网络数据的程序
- 前置知识:
- HTML、JavaScript 基础
- 网络协议 (POST/GET)
- 正则表达式:re
- xpath
- 应用:
- 数据挖掘、机器学习、图像处理等科学研究领域的数据获取
- 产品研发的市场数据
- 舆情监控数据
基本流程
- HTML 请求
- 工具:urllib3\requests\mechanize\selenium\splinter
- 数据请求:
- html:静态页面或动态页面,在网页上看到的内容都可以以特定的 HTML 标签得到。
- js:找到包含内容的 js 代码串,然后通过正则表达式获得相应的内容,而不是解析 HTML 标签。
- ajax:分页形式显示在网页上,需对网页进行交互操作后得到内容
- app 的抓包
- HTML 解析:
- 工具:lxml\beautifulsoup4\re\pyquery
- JSON 解析
- css 选择器 class/id 定位
- XPATH 路径选择
- 正则表达式匹配
- 存储
- 缓存:Redis
- 结构化存储至数据库、CSV、json
- 网页判重
- 信息抽取
- 更新
- 爬虫速度
- 网络阻塞:
- 多线程、任务调度、消息队列、分布式爬虫、
- CPU 负载:
- 反爬虫破解
- 请求头中的字段,如 User-Agent、refer 等,
- 用户登录认证
- 用户行为:后台对访问 IP 或 User-Agent 有统计,超过一定阈值进行封锁,此时需要代理服务器,每隔几次请求,切换代理服务器。得控制好度,别把别人的网站搞坏了引起纠纷。
- 代理池:保证爬虫的稳定性
- API 接口
- 数据为 ajax 请求,根据请求与确定的参数,模拟 http 请求,从响应中得到数据。
- JS 加密反爬:ajax 动态加载数据 :Selenium+ChromeDriver
- 基于 JavaScript 的反爬虫手段,主要是在响应数据页面之前,先返回一段带有 JavaScript 代码的页面,用于验证访问者有无 JavaScript 的执行环境,以确定使用的是不是浏览器
- webdriver 识别
- 图像识别、验证码识别,
tesseract-ocr - APP 上的数据
- mitmproxy 抓包
- 反编译 APK,分析鉴权与 API
- 安全性
- http//网址/robots.txt
- 站长工具
- 匹配方式
- re
- xpath
- 进阶
- 爬虫策略
- 异步 IO
- 分布式爬虫
相关工具
- 静态页面,就上 requests +lxml/beautifulSoup
- 动态网站:大量js动态渲染,就上 Playwright/selenium
- 复杂的、大型的就上scrapy
webscrapyer:一个谷歌插件,点点鼠标抓取数据。portia:Docker 下的开源可视化爬虫工具pyspidercreepypyquery
socket
socket:套接字的相关,实现不同主机的进程间的数据交换。
- 传输层的网络编程,具体网络相关参考计算机网络的相关知识
- 这里主要讨论 Python 的 TCP 通信
- UDP 端口与 TCP 端口不冲突,即 UDP 的 9999 端口与 TCP 的 9999 端口可以各自绑定
- TCP:建立可靠连接,通信双方都可以以流的形式发送数据。
- UDP:无连接,只需要知道对方的 IP 地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。
Socket=IP address+TCP/UDP+port:IP 地址 + 协议 + 端口号- UDP 传输数据不可靠,但比 TCP 速度快。
1 | conn = socket.socket( |
基本使用
- 客户端:主动发起连接的:主动连接服务器的 IP 与指定端口
- 服务端:被动响应的:监听指定端口,对每个新的连接创建一个线程或进程来处理
- 同一个端口,只能被一个 Socket 绑定
- socket 连接具有唯一性:四项:服务器地址、服务器端口、客户端地址、客户端端口。
1 | import socket |
1 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
requests
requests:请求网页信息的模块,基于 urllib 的 Python 第三方库,但比 urllib 更加方便易用。
urlib:用过 requests,再也不会考虑 urllib
- 特点:
- http 请求:自定义请求头、设置代理、支持重定向、支持 Session、Cookie 持久化、支持超时设置, 对 post 数据自动 urlencode;
- http 响应:可获取响应的所有数据,如状态码、自动解码的响应内容、响应头中的各个字段;
- 内置 JSON 解码器。
get-post
1 | import requests |
1 | 如果网页的编码为 gbk,需要这样处理: |
传入数据格式
1 | r = requests.post( |
- 代理设置:频繁抓取很容易被服务器屏蔽掉,或需爬取墙外的数据,需使用代理
1 | import requests |
- 超时设置:requests 发送请求时,线程会阻塞,直到有响应返回时才会处理后续逻辑,为避免服务器一直未响应,需:
1 | import requests |
cookie 处理
- cookie 字符串放在 headers 中
1 | headers={ |
- 将 cookie 字典传入请求方法的 cookies 参数接收
1 | cookies = {"cookie 的 name":"cookie 的 value"} |
- session
1 | # 以 session 进行会话保持 |
lxml
lxml:解析网页信息的模块,使用 xpath 解析器解析 htmlBeautifulsoup4:BS4:优雅地处理 html,but 打不过 lxml,OUT
- bs4 的罗嗦与不好维护,lxml 具有基于 C 的超越 bs4 的速度
1 | pip3 install Beautifulsoup4 |
1 | from lxml import etree |
PyQuery:网页解析库,对 jQuery 熟悉者十分友好,不熟悉 jQuery,OUT.
结构
Element 类
- XML 处理的核心类,看作 xml 的节点
- 节点操作、节点属性操作、节点内文本操作
1 | root=etree.Element('root') #参数即节点名称 |
scrapy
scrapy:一套成熟的 Python 爬虫框架,抓取网站数据并提取结构化数据。
- 教程:Scrapy1.5 超易懂
- 轻量、简单、易用
- 基于 Twisted 异步网络库处理网络通迅
- scrapy+rq-queue 然后用 redis 来作队列就好。
- 特点:
- Twisted 异步网络库处理网络通讯
- url 管理、url 解析
概述
组件
引擎:engine:处理整个系统的数据流,触发事务(框架核心)
调度器:Scheduler:调度 engine 发来的请求,将之加入队列,在 engine 再次请求时返回
- 决定下一个要抓取的网址,并去除重复网址
下载器:Downloader:下载网页数据,并将其传给 engine(基于 twisted)
爬虫:Spiders:制定网页解析规则,从特定的网页中提取需要的信息,如实体(item)和 urls
项目管道:Pipeline:处理爬虫从网页中抽取的实体,主要为:持久化实体、验证有效性,清除不必要的信息
- 负责处理有蜘蛛从网页中抽取的项目,他的主要任务是清晰、验证和存储数据。当页面被蜘蛛解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
- 对 pares 取出的数据进行去重、清洗
- 清理 HTML 数据
- 验证爬取的数据 (检查 item 包含某些字段)
- 查重 (并丢弃)
- 将爬取结果保存到数据库中:处理 engine 解析处理的 spider 返回的数据,并将数据持久化,即存入数据库或文件
下载器中间件:Downloader Middlewares:处于引擎与下载器之间,以钩子的形式处理 Scrapy 引擎与下载器之间的请求及响应。
位于 Scrapy 引擎和下载器之间的钩子框架,主要是处理 Scrapy 引擎与下载器之间的请求及响应。
代替接收请求、处理数据的下载以及将结果响应给 engine
实现一系列操作,例如使用代理、cookies、设置 timeout 等。
1
2
3process_request(request, spider)
process_response(request, response, spider)
process_exception(request, exception, spider)
**爬虫中间件 (Spider Middlewares)***介于 Scrapy 引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
- 介于 Scrapy 引擎和蜘蛛之间的钩子框架,主要工作是处理蜘蛛的响应输入和请求输出。
- 调度、发送、接收、解析等一系列工作
**调度中间件 (Scheduler Middewares)***介于 Scrapy 引擎和调度之间的中间件,从 Scrapy 引擎发送到调度的请求和响应。
- 介于 Scrapy 引擎和调度之间的中间件,从 Scrapy 引擎发送到调度的请求和响应。
流程:
- 引擎从调度器中取出一个链接 (URL) 用于接下来的抓取
- 引擎把 URL 封装成一个请求 (Request) 传给下载器
- 下载器把资源下载下来,并封装成应答包 (Response)
- 爬虫解析 Response
- 解析出实体(Item),则交给实体管道进行进一步的处理
- 解析出的是链接(URL),则把 URL 交给调度器等待抓取
spider
1
2
3
4
5
6name
allowed_domains
start_urls
custom_settings
crawler
settings
相关工具
scrapyd:通过 scrapyd-client 将不同网站的爬虫上传到不同的服务器,只需在 windows 上就能启动爬虫、暂停爬虫、修改爬虫
一台机子上既装 scrapyd 又装 scrapyd-client 是毫无意义且浪费资源的
基本命令
- 新建项目:
scrapy start project project1
1 | tutorial/ |
1 | scrapy genspider [options] <蜘蛛 name> <domain-taobao.com> # 根据蜘蛛模板创建蜘蛛 |
1 | 创建项目 |
poem-spiders 下写蜘蛛
1 | import scrapy |
运行
1 | scrapy crawl poem_spider #poem_spider 为蜘蛛的 name |
scrapy shell
- shell 支持你在调试的过程中使用爬虫内的几乎所有工具,例如选择器等。
1 | scrapy shell http://www.baidu.com |
1 | scrapy shell # 调试用 |
基本流程
打开页面
1 | import scrapy |
提取数据 -Selector
Scrapy 的数据提取器:选择器 Selector,进行 CSS、Xpath 或正则的选择,
- SelectorList 和 Selecto
- 建立在 lxml 库上,解析速度与精度差不多
1 | from scrapy.selector import Selector |
- .extract_first():提取 SelectorList 对象中第一个元素的内容。
- .extract():如果是 SelectorList 对象使用,则返回包含内容的列表;如果是 Selector 使用,则返回它的内容。
css 提取
1 | response.css('title').extract()[0] #css 为 title 的列表的第一项 |
- 常见 css 表达式提取
1 | response.css('.class_name a::attr(href)').extract() |
1 | .class #class 选择器 |
| 选择器 | 示例 | 示例说明 | CSS |
|---|---|---|---|
| .class | .intro | 选择所有 class=”intro” 的元素 | 1 |
| #id | #firstname | 选择所有 id=”firstname” 的元素 | 1 |
| * | * | 选择所有元素 | 2 |
| element | p | 选择所有 元素 | 1 |
| element,element | div,p | 选择所有 元素和 元素 | 1 |
| element element | div p | 选择 元素内的所有 元素 | 1 |
| element>element | div>p | 选择所有父级是 元素的 元素 | 2 |
| element+element | div+p | 选择所有紧接着 元素之后的 元素 | 2 |
| [attribute] | [target] | 选择所有带有 target 属性元素 | 2 |
| [attribute=value] | [target=-blank] | 选择所有使用 target=”-blank” 的元素 | 2 |
| [attribute~=value] | [title~=flower] | 选择标题属性包含单词 “flower” 的所有元素 | 2 |
| [attribute|=language] | [lang|=en] | 选择一个 lang 属性的起始值=”EN” 的所有元素 | 2 |
| :link | a:link | 选择所有未访问链接 | 1 |
| :visited | a:visited | 选择所有访问过的链接 | 1 |
| :active | a:active | 选择活动链接 | 1 |
| :hover | a:hover | 选择鼠标在链接上面时 | 1 |
| :focus | input:focus | 选择具有焦点的输入元素 | 2 |
| :first-letter | p:first-letter | 选择每一个 元素的第一个字母 | 1 |
| :first-line | p:first-line | 选择每一个 元素的第一行 | 1 |
| :first-child | p:first-child | 指定只有当 元素是其父级的第一个子级的样式。 | 2 |
| :before | p:before | 在每个 元素之前插入内容 | 2 |
| :after | p:after | 在每个 元素之后插入内容 | 2 |
| :lang(language) | p:lang(it) | 选择一个 lang 属性的起始值=”it” 的所有 元素 | 2 |
| element1~element2 | p~ul | 选择 p 元素之后的每一个 ul 元素 | 3 |
| [attribute^=value] | a[src^=”https”] | 选择每一个 src 属性的值以 “https” 开头的元素 | 3 |
| [attribute$=value] | a[src$=”.pdf”] | 选择每一个 src 属性的值以 “.pdf” 结尾的元素 | 3 |
| [attribute*=value] | a[src*=”runoob”] | 选择每一个 src 属性的值包含子字符串 “runoob” 的元素 | 3 |
| :first-of-type | p:first-of-type | 选择每个 p 元素是其父级的第一个 p 元素 | 3 |
| :last-of-type | p:last-of-type | 选择每个 p 元素是其父级的最后一个 p 元素 | 3 |
| :only-of-type | p:only-of-type | 选择每个 p 元素是其父级的唯一 p 元素 | 3 |
| :only-child | p:only-child | 选择每个 p 元素是其父级的唯一子元素 | 3 |
| :nth-child(n) | p:nth-child(2) | 选择每个 p 元素是其父级的第二个子元素 | 3 |
| :nth-last-child(n) | p:nth-last-child(2) | 选择每个 p 元素的是其父级的第二个子元素,从最后一个子项计数 | 3 |
| :nth-of-type(n) | p:nth-of-type(2) | 选择每个 p 元素是其父级的第二个 p 元素 | 3 |
| :nth-last-of-type(n) | p:nth-last-of-type(2) | 选择每个 p 元素的是其父级的第二个 p 元素,从最后一个子项计数 | 3 |
| :last-child | p:last-child | 选择每个 p 元素是其父级的最后一个子级。 | 3 |
| :root | :root | 选择文档的根元素 | 3 |
| :empty | p:empty | 选择每个没有任何子级的 p 元素(包括文本节点) | 3 |
| :target | #news:target | 选择当前活动的#news 元素(包含该锚名称的点击的 URL) | 3 |
| :enabled | input:enabled | 选择每一个已启用的输入元素 | 3 |
| :disabled | input:disabled | 选择每一个禁用的输入元素 | 3 |
| :checked | input:checked | 选择每个选中的输入元素 | 3 |
| :not(selector) | :not(p) | 选择每个并非 p 元素的元素 | 3 |
| ::selection | ::selection | 匹配元素中被用户选中或处于高亮状态的部分 | 3 |
| :out-of-range | :out-of-range | 匹配值在指定区间之外的 input 元素 | 3 |
| :in-range | :in-range | 匹配值在指定区间之内的 input 元素 | 3 |
| :read-write | :read-write | 用于匹配可读及可写的元素 | 3 |
| :read-only | :read-only | 用于匹配设置 “readonly”(只读) 属性的元素 | 3 |
| :optional | :optional | 用于匹配可选的输入元素 | 3 |
| :required | :required | 用于匹配设置了 “required” 属性的元素 | 3 |
| :valid | :valid | 用于匹配输入值为合法的元素 | 3 |
| :invalid | :invalid | 用于匹配输入值为非法的元素 | 3 |
明确目标 -items.py
爬取 -xxspider.py
保存数据 -pipelines.py
管道:在 settings.py 中加入两行代码:
1 | FEED_URI=u'/Users/apple/Documents/jianshu-hot.csv' |
selenium
selenium:模拟浏览器的数据采集,web 应用程序自动化测试框架,主要用于 web 应用程序的测试。模拟真实用户对浏览器进行操作,适用于 js 渲染多的
爬虫工具,对于由 js 或 ajax 生成 的网页,可使用 selenium 进行动态网页加载。
遇到的问题
Message: ‘chromedriver’ executable needs to be in PATH
chrome 浏览器输入
chrome://version/查看 chrome 版本chromedriver 下载对应版本的 driver,如
79.0.3845.88chromedriver_win32.zip解压至 chrome 安装的指定文件夹下,如
C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe1
2chrome_drive=r"C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe"
browser = webdriver.Chrome(executable_path=chrome_drive)#声明浏览器
005-you-get
you-get:一个 YouTube/Youku/Niconico 视频下载器,使用 Python3 编写。
1 | 常用 |
pytesser
pytesser:验证码破解
newscatcher
newscatcher:获取新闻资讯的工具包,它时刻监控者成千上万个新闻媒体并对其进行聚合,它包含丰富的 API 接口,开发者可以通过时间、新闻源、关键字等方式来获取新闻资讯。
- 获取自己想的新闻资讯
- 数据源丰富
urllib
urllib:已被 request 替代,用于执行各种 http 请求,获取网络资源
- 不好用、不实用
- Get:urllib 的 request 模块以 Get 请求发送到指定页面,返回的 http 响应为 URL 内容
1 | from urllib import request |
- Post:以 Post 将参数 data 以 bytes 形式传入请求。
username=xxx&password=xxx - handler:如果还需要更复杂的控制,比如通过一个 Proxy 去访问网站,我们需要利用
ProxyHandler来处理,
aiohttp
aiohttp:基于 asyncio 的异步 HTTP 网络库。
