Python-Requests与BeautifulSoup实战 Requests 基本使用库Python 提供了多个用来编写爬虫程序的库有一个重要网络请求库就是 Requests 库这个库的宗旨是让 HTTP 服务于人类。Requests 是 Python 的第三方库它的安装非常简便如下所示pip install requestsRequests 库是在 urllib 的基础上开发而来它使用 Python 语言编写并且采用了 Apache2 Licensed一种开源协议的 HTTP 库。与 urllib 相比Requests 更加方便、快捷因此在编写爬虫程序时 Requests 库使用较多接下来我们一起来学习requests库。常用请求方法requests.get()该方法用于 GET 请求表示向网站发起请求获取页面响应对象。语法如下res requests.get(url, headersheaders, params, timeout)参数说明如下url要抓取的 url 地址。headers用于包装请求头信息。params请求时携带的查询字符串参数。timeout超时时间超过时间会抛出异常。具体使用示比如下import requests url http://baidu.com response requests.get(url) print(response) # print(response.text) 可以查看请求具体的返回数据信息输出结果Response [200]200 是请求返回状态码表示请求成功。如果我们想要请求时带上请求参数使用也非常的简单这里使用到了 http://httpbin.org当我们请求http://httpbin.org/get 时会返回具体的请求信息示比如下所示import requests data {name: 渣男教父, url: www.ybf.com} response requests.get(http://httpbin.org/get, paramsdata) # 直接拼接参数也可以 # response requests.get(http://httpbin.org/get?nameybfage33) # 调用响应对象text属性获取文本信息 print(response.text)输出结果{ args: { name: \u6709\u9738\u592b, url: www.ybf.com }, headers: { Accept: */*, Accept-Encoding: gzip, deflate, Host: httpbin.org, User-Agent: python-requests/2.25.1, X-Amzn-Trace-Id: Root1-62216f84-2054fe065f625d4928c77cfa }, origin: 175.0.53.160, url: http://httpbin.org/get?name\u6709\u9738\u592burlwww.ybf.com }requests.post()该方法用于 POST 请求先由用户向目标 url 提交数据然后服务器返回一个 HttpResponse 响应对象语法如下response requests.post(url, data{请求体的字典})示比如下所示import requests url https://fanyi.baidu.com # 百度翻译 # post请求体携带的参数可通过开发者调试工具查看 # 查看步骤NetWork选项-Headers选项-Form Data data {from: zh, to: en, query: 欢迎参加跟渣男教父学编程课堂} response requests.post(url, datadata) print(response) # print(response.text) 可以查看请求具体的返回数据信息输出结果Response [200]chrome中查看 Form Data 的步骤非常的简单如下图所示响应对象属性当我们使用 Requests 模块向一个 URL 发起请求后会返回一个 HttpResponse 响应对象该对象具有以下常用属性常用属性说明encoding查看或者指定响应字符编码status_code返回HTTP响应码url查看请求的 url 地址headers查看请求头信息cookies查看cookies 信息text以字符串形式输出content以字节流形式输出若要保存下载图片需使用该属性。使用示比如下所示import requests response requests.get(http://www.baidu.com) print(response.encoding) response.encoding utf-8 # 更改为utf-8编码 print(response.status_code) # 打印状态码 print(response.url) # 打印请求url print(response.headers) # 打印头信息 print(response.cookies) # 打印cookie信息 print(response.text) # 以字符串形式打印网页源码 print(response.content) # 以字节流形式打印输出结果# 编码格式 ISO-8859-1 # 响应码 200 # url地址 http://www.baidu.com/ # 请求头信息 {Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform, Connection: keep-alive, Content-Encoding: gzip, Content-Type: text/html, Date: Fri, 04 Mar 2022 02:04:36 GMT, Last-Modified: Mon, 23 Jan 2017 13:27:57 GMT, Pragma: no-cache, Server: bfe/1.0.8.18, Set-Cookie: BDORZ27315; max-age86400; domain.baidu.com; path/, Transfer-Encoding: chunked} # 查看cookies信息 RequestsCookieJar[Cookie BDORZ27315 for .baidu.com/] ...内容过长此处省略后两项输出在使用 requests请求网页时我们可以很方便的拿到响应对象的各种信息实际使用中可以很灵活的进行操作。实用请求参数SSL认证-verify参数SSL 证书是数字证书的一种类似于驾驶证、护照和营业执照。因为配置在服务器上也称为 SSL 服务器证书。SSL 证书遵守 SSL 协议由受信任的数字证书颁发机构 CA电子认证服务颁发。 SSL 具有服务器身份验证和数据传输加密功能。verify 参数的作用是检查 SSL 证书认证参数的默认值为 True如果设置为 False 则表示不检查 SSL证书此参数适用于没有经过 CA 机构认证的 HTTPS 类型的网站。其使用格式如下response requests.get(urlurl, paramsparams, headersheaders, verifyFalse)代理IP-proxies参数一些网站为了限制爬虫从而设置了很多反爬策略其中一项就是针对 IP 地址设置的。比如访问网站超过规定次数导致流量异常或者某个时间段内频繁地更换浏览器访问存在上述行为的 IP 极有可能被网站封杀掉。代理 IP 就是解决上述问题的它突破了 IP 地址的访问限制隐藏了本地网络的真实 IP而使用第三方 IP 代替自己去访问网站。1. 代理IP池通过构建代理 IP 池可以让你编写的爬虫程序更加稳定从 IP 池中随机选择一个 IP 去访问网站而不使用固定的真实 IP。总之将爬虫程序伪装的越像人它就越不容易被网站封杀。当然代理 IP 也不是完全不能被察觉通过端口探测技等术识仍然可以辨别。其实爬虫与反爬虫永远相互斗争的就看谁的技术更加厉害。2. proxies参数Requests 提供了一个代理 IP 参数proxies 该参数的语法结构如下proxies { 协议类型(http/https): 协议类型://IP地址:端口号 }下面构建了两个协议版本的代理 IP示比如下proxies { http: http://IP:端口号, https: https://IP:端口号 }3. 代理IP使用下面通过简单演示如何使用proxies 参数示比如下import requests url http://httpbin.org/get headers {User-Agent: Mozilla/5.0} proxies {http: http://1.194.238.242:3828} # 网上找的免费代理ip html requests.get(url, proxiesproxies, headersheaders, timeout10).text print(html)输出结果{ args: {}, headers: { Accept: */*, Accept-Encoding: gzip, deflate, Cache-Control: max-age259200, Host: httpbin.org, User-Agent: Mozilla/5.0, X-Amzn-Trace-Id: Root1-622b1032-3ef292bc349cd9ca3d57e39f }, origin: 1.194.238.242, url: http://httpbin.org/get }由于上述示例使用的是免费代理 IP因此其质量、稳定性较差可能会随时失效。如果想构建一个稳定的代理 IP 池就需要花费成本。4. 付费代理IP网上有许多提供代理 IP 服务的网站比如快代理、携趣代理等。这些网站也提供了相关文档说明以及 API 接口爬虫程序通过访问 API 接口就可以构建自己的代理 IP 池。付费代理 IP 按照资源类型可划分为开发代理、私密代理、隧道代理、独享代理其中最常使用的是开放代理与私密代理。开放代理开放代理是从公网收集的代理服务器具有 IP 数量大使用成本低的特点全年超过 80% 的时间都能有 3000 个以上的代理 IP 可供提取使用。私密代理私密代理是基于云主机构建的高品质代理服务器为您提供高速、可信赖的网络代理服务。私密代理每天可用 IP 数量超过 20 万个可用率在 95 %以上1 次可提取 IP 数量超过 700 个可以为爬虫业务提供强大的助力。付费代理的收费标准根据 IP 使用的时间长短以及 IP 的质量高低从几元到几百元不等网上有很多提供免费代理 IP 的网站不过想找到一个质量较高的免费代理好比大海捞针。用户认证-auth参数Requests 提供了一个auth 参数该参数的支持用户认证功能也就是适合那些需要验证用户名、密码的网站。auth 的参数形式是一个元组其格式如下auth (username, password)其使用示比如下所示import requests # 导入requests模块 # 定义请求地址 url http://sck.rjkflm.com:666/spider/auth/ ah (admin, admin) response requests.get(urlurl, authah) if response.status_code 200: print(response.text)Requests 库实战应用接下来我们学习使用 Requests 库下载图片。首先打开天堂图片网https://www.ivsky.com/然后使用 Chrome 开发者工具随意查看一张图片的源地址如下图所示图片地址为img src//img.ivsky.com/img/tupian/slides/202109/09/xiniu-001.jpg可以将上述 url 粘贴至浏览器地址栏进行验证。当我们确定图片地址后就可以使用 requests 库进行编码了import requests url https://img.ivsky.com/img/tupian/slides/202109/09/xiniu-001.jpg headers {User-Agent: Mozilla/4.0} html requests.get(urlurl, headersheaders).content # 读取图片需要使用content属性 with open(D:/requests_img.jpg, wb) as f: # 以二进制的方式下载图片 f.write(html)最后在D盘目录下可以找到已经下载好的图片。网页解析 BeautifulSoup 库BeautifulSoup 简称 BS4其中 4 表示版本号是一个 Python 第三方库它可以从 HTML 或 XML 文档中快速地提取指定的数据。Beautiful Soup 语法简单使用方便并且容易理解可以快速地学习并掌握它。下面我们讲解 BS4 的基本语法。由于 BeautifulSoup 是第三方库因此需要单独下载下载方式非常简单执行以下命令即可安装pip install bs4由于 BS4 解析页面时需要依赖文档解析器所以还需要安装 lxml 作为解析库pip install lxmlPython 也自带了一个文档解析库 html.parser但是其解析速度要稍慢于 lxml。除了上述解析器外还可以使用 html5lib 解析器安装方式如下pip install html5lib该解析器生成 HTML 格式的文档但速度较慢。BS4解析对象创建 BS4 解析对象是万事开头的第一步这非常地简单语法格式如下所示# 导入解析包 from bs4 import BeautifulSoup # 创建beautifulsoup解析对象 soup BeautifulSoup(html_doc, html.parser)上述代码中html_doc 表示要解析的文档而 html.parser 表示解析文档时所用的解析器此处的解析器也可以是 lxml 或者 html5lib示例代码如下所示# -*- coding: UTF-8 -*- from bs4 import BeautifulSoup html_doc htmlheadtitle跟渣男教父学编程/title/headbodyp classtitlebwww.youbafu.com/b/pp classwebsite跟渣男教父学编程a hrefhttp://youbafu.com/python/ idlink1python教程/aa hrefhttp://youbafu.com/c/ idlink2c语言教程/a soup BeautifulSoup(html_doc, html.parser) # prettify()用于格式化输出html/xml文档 print(soup.prettify())输出结果html head title 跟渣男教父学编程 /title /head body p classtitle b www.youbafu.com /b /p p classwebsite 跟渣男教父学编程 a hrefhttp://youbafu.com/python/ idlink1 python教程 /a a hrefhttp://youbafu.com/c/ idlink2 c语言教程 /a /p /body /html如果是外部文档您也可以通过 open() 的方式打开读取语法格式如下soup BeautifulSoup(open(html_doc.html, encodingutf8), lxml)BS4常用语法接下来一起学习一下爬虫中经常用到的 BS4 解析方法。Beautiful Soup 将 HTML 文档转换成一个树形结构该结构有利于快速地遍历和搜索 HTML 文档。下面使用树状结构来描述一段 HTML 文档html headtitle跟渣男教父学编程/title/head body h1www.bpsend.net/h1 pb一个学习编程的网站/b/p /body /html文档树中的每个节点都是 Python 对象这些对象大致分为四类Tag , NavigableString , BeautifulSoup , Comment 。其中使用最多的是 Tag 和 NavigableString。Tag标签类HTML 文档中所有的标签都可以看做 Tag 对象。NavigableString字符串类指的是标签中的文本内容使用 text、string、strings 来获取文本内容。BeautifulSoup表示一个 HTML 文档的全部内容您可以把它当作一个人特殊的 Tag 对象。Comment表示 HTML 文档中的注释内容以及特殊字符串它是一个特殊的 NavigableString。Tag节点标签Tag是组成 HTML 文档的基本元素。在 BS4 中通过标签名和标签属性可以提取出想要的内容。看一组简单的示例from bs4 import BeautifulSoup soup BeautifulSoup(p classWeb site urlbwww.youbafu.com/b/p, html.parser) # 获取整个p标签的html代码 print(soup.p) # 获取p标签 print(soup.p.b) # 获取b标签 print(soup.p.text) # 获取p标签内容使用NavigableString类中的string、text、get_text() print(soup.p.attrs) # 返回一个字典里面是多有属性和值 print(type(soup.p)) # 查看返回的数据类型 print(soup.p[class]) # 根据属性获取标签的属性值返回值为列表 soup.p[class] [Web, Site] # 给class属性赋值,此时属性值由列表转换为字符串 print(soup.p)输出结果如下soup.p输出结果: p classWeb site urlbwww.bpsend.net/b/p soup.p.b输出结果 bwww.bpsend.net/b soup.p.text输出结果 www.bpsend.net soup.p.attrs输出结果 {class: [Web, site, url]} type(soup.p)输出结果 class bs4.element.Tag soup.p[class]输出结果 [Web, site, url] class属性重新赋值 p classWeb site urlbwww.bpsend.net/b/p遍历节点Tag 对象提供了许多遍历 tag 节点的属性比如 contents、children 用来遍历子节点parent 与 parents 用来遍历父节点而 next_sibling 与 previous_sibling 则用来遍历兄弟节点 。示比如下html_doc htmlheadtitle跟渣男教父学编程/title/headbodyp classtitlebwww.youbafu.com/b/pp classwebsite一个学习编程的网站/pa hrefhttp://www.bpsend.net/python/ idlink1python教程/a a hrefhttp://www.bpsend.net/c/ idlink2c语言教程/a /body/html soup BeautifulSoup(html_doc, html.parser) body_tag soup.body print(body_tag) print(body_tag.contents) # 以列表的形式输出所有子节点输出结果bodyp classtitlebwww.youbafu.com/b/pp classwebsite一个学习编程的网站 /pa hrefhttp://www.youbafu.com/python/ idlink1python教程/a a hrefhttp://www.youbafu.com/c/ idlink2c语言教程/a /body # 以列表的形式输出 [p classtitlebwww.youbafu.com/b/p, p classwebsite一个学习编程的网站 /p, a hrefhttp://www.youbafu.com/python/ idlink1python教程/a, \n, a hrefhttp://www.youbafu.com/c/ idlink2c语言教程/a, ]Tag 的 children 属性会生成一个可迭代对象可以用来遍历子节点示比如下for child in body_tag.children: print(child)输出结果p classtitlebwww.youbafu.com/b/p p classwebsite一个学习编程的网站/p a hrefhttp://www.youbafu.com/python/ idlink1python教程/a a hrefhttp://www.youbafu.com/c/ idlink2c语言教程/afind_all() 与 find()find_all() 与 find() 是解析 HTML 文档的常用方法它们可以在 HTML 文档中按照一定的条件相当于过滤器查找所需内容。find() 与 find_all() 的语法格式相似希望大家在学习的时候可以举一反三。BS4 库中定义了许多用于搜索的方法find() 与 find_all() 是最为关键的两个方法其余方法的参数和使用与其类似。find_all()find_all() 方法用来搜索当前 tag 的所有子节点并判断这些节点是否符合过滤条件最后以列表形式将符合条件的内容返回语法格式如下find_all(name, attrs, recursive, text, limit)参数说明name查找所有名字为 name 的 tag 标签字符串对象会被自动忽略。attrs按照属性名和属性值搜索 tag 标签注意由于 class 是 Python 的关键字所以要使用 class_。recursivefind_all() 会搜索 tag 的所有子孙节点设置 recursiveFalse 可以只搜索 tag 的直接子节点。text用来搜文档中的字符串内容该参数可以接受字符串 、正则表达式 、列表、True。limit由于 find_all() 会返回所有的搜索结果这样会影响执行效率通过 limit 参数可以限制返回结果的数量。find_all() 使用示比如下from bs4 import BeautifulSoup html_doc htmlheadtitle跟渣男教父学编程/title/headbodyp classtitlebwww.bpsend.net/b/pp classwebsite一个学习编程的网站/pa hrefhttp://www.bpsend.net/python/ idlink1python教程/aa hrefhttp://www.bpsend.net/c/ idlink2c语言教程/aa hrefhttp://www.bpsend.net/django/ idlink3django教程/ap classvip加入我们阅读所有教程/pa hrefhttp://www.bpsend.net/vip idlink4成为vip/a # 创建soup解析对象 soup BeautifulSoup(html_doc, html.parser) print(soup.find_all(a)) # 查找所有a标签并返回 print(soup.find_all(a, limit2)) # 查找前两条a标签并返回按照标签属性以及属性值查找 HTML 文档如下所示print(soup.find_all(p, class_website)) print(soup.find_all(idlink4))正则表达式、列表以及 True 也可以当做过滤条件使用示比如下import re # 查找tag标签 print(soup.find_all([b, a])) # 正则表达式匹配id属性值 print(soup.find_all(a, idre.compile(r.\d))) print(soup.find_all(idTrue)) # True可以匹配任何值下面代码会查找所有tag并返回相应的tag名称 for tag in soup.find_all(True): print(tag.name, end ) # 输出所有以b开始的tag标签BS4 为了简化代码为 find_all() 提供了一种简化写法如下所示soup.find_all(a)上述两种的方法的输出结果是相同的。find()find() 方法与 find_all() 方法类似不同之处在于 find() 只返回第一个匹配的元素而不是列表。语法格式如下find(name, attrs, recursive, text)文档总结一、核心知识点回顾1. Requests 库到底能做什么Requests 就是帮你向网站要数据的工具。打个比方你去餐厅点菜服务员帮你下单 → Requests 帮你向服务器发请求服务员把菜端给你 → Requests 把网页内容返回给你核心功能requests.get() → 获取网页最常用 requests.post() → 提交数据登录、搜索等 response.text → 拿到文本HTML、JSON response.content → 拿到二进制图片、文件2. GET vs POST 请求对比项GETPOST用途获取数据提交数据参数位置URL 里?keyvalue请求体里参数参数params{}data{}例子搜索、浏览页面登录、发表评论长度限制有URL长度限制无使用场景浏览网页、搜索商品 → GET登录账号、提交表单 → POST3. 响应对象常用属性response requests.get(url) response.status_code # 状态码200成功、404找不到、403禁止 response.text # 文本内容字符串 response.content # 二进制内容下载图片用 response.url # 实际请求的URL可能有跳转 response.headers # 响应头信息 response.encoding # 编码格式 response.cookies # Cookie信息状态码速查200—— 成功301/302—— 重定向跳转了400—— 请求参数错误403—— 被拒绝可能触发反爬404—— 页面不存在500—— 服务器错误4. 实用请求参数参数作用示例headers伪装浏览器{User-Agent: ...}paramsGET 请求参数{wd: python}dataPOST 请求参数{username: xxx}timeout超时时间timeout10proxies代理 IP{http: http://IP:端口}verifySSL 认证verifyFalse跳过证书检查auth用户认证auth(user, pass)5. BeautifulSoup 基础BS4 就是帮你从 HTML 里找数据的工具。创建解析对象from bs4 import BeautifulSoup soup BeautifulSoup(html, html.parser) # 或 lxml四大对象Tag—— 标签a,div等NavigableString—— 标签里的文本BeautifulSoup—— 整个文档Comment—— 注释内容6. BS4 查找元素方法作用返回值find()找第一个匹配的Tag 对象find_all()找所有匹配的列表select()CSS 选择器列表常用查找方式# 按标签名 soup.find(a) soup.find_all(a) # 按 class soup.find(div, class_item) soup.find_all(div, class_item) # 按 id soup.find(idkw) # 按属性 soup.find(a, hrefhttp://example.com) # 获取文本 tag.text tag.get_text() tag.string # 获取属性 tag[href] tag[src] tag.get(class) find() vs find_all() # find() 只返回第一个 result soup.find(a) print(result) # a href...第一个链接/a # find_all() 返回所有列表 results soup.find_all(a) print(results) # [a..., a..., a...] # 限制数量 results soup.find_all(a, limit3) # 只返回前3个7. 下载图片/文件import requests # 下载图片 url https://example.com/image.jpg response requests.get(url) # 用 content二进制不是 text with open(image.jpg, wb) as f: f.write(response.content)关键点下载图片/文件 → 用response.content获取网页文本 → 用response.text打开文件用wb模式二进制写入二、常见坑与解决方案坑1response.text 乱码症状打印出来是乱码 原因编码识别错误默认 ISO-8859-1 解决response.encoding utf-8 或 response.apparent_encoding坑2下载图片打不开症状下载的图片无法打开 原因用了 response.text 而不是 response.content 解决下载二进制文件必须用 .content坑3find_all() 返回空列表症状soup.find_all(div, class_item) 返回 [] 原因1class 名写错了 原因2HTML 结构和你想的不一样 解决先 print(soup) 看看实际 HTML 长什么样坑4class 是 Python 关键字症状soup.find(div, classitem) 报错 原因class 是 Python 保留字 解决用 class_加下划线坑5代理 IP 不可用症状requests.get() 超时或报错 原因免费代理经常失效 解决换代理或设置 timeout 避免卡死坑6忘记加 User-Agent症状返回 403 或空内容 原因网站识别出你是爬虫 解决加 headers{User-Agent: ...}三、学习建议Requests 优先于 urllib—— 代码简洁功能强大BS4 适合新手—— 比正则直观比 lxml 简单先打印 response—— 看看状态码和内容再决定怎么处理下载文件用 content—— 别用 text否则文件会损坏加 timeout 保平安—— 避免程序永远卡住先找单个再找多个—— 先用 find() 测试再用 find_all() 批量