从上一章开始,我们现在了解了什么是网络抓取,现有的核心开发技术是什么,我们可以计划在哪里或如何找到我们正在寻找的信息。
Web 抓取需要使用脚本或程序来实现和部署工具和技术。Python 编程语言由一组庞大的库组成,这些库适合与 web 进行交互并用于抓取;我们还将探索和搜索要从 web 中提取的内容
本章还将详细介绍如何使用 Python 库,如requests和urllib。
我们将特别了解以下主题:
- 设置 Python 及其所需库
requests和urllib以加载 URL requests和urllib的详细概述- 实现 HTTP 方法(
GET/POST)
We assume that you have some prior basic experience of using the Python programming language. If not, then please refer to Python tutorials from W3schools (https://www.w3schools.com/python/default.asp), Python course (https://python-course.eu/), or search Google for learn Python programming.
我们将使用已安装在 Windows 操作系统上的 Python 3.7.0。代码编辑器有很多选择;选择一个便于使用和处理本章代码示例中使用的库的库。我们将使用 PyCharm(社区版https://www.jetbrains.com/pycharm/download/download-thanks.html?platform=windows 来自 JetBrains 和 Python IDLE(的&代码=PCChttps://www.python.org/downloads/ )并排。
要按照本章操作,您需要安装以下应用程序:
- Python 3.7.*或适用于您的操作系统的最新版本:https://www.python.org/downloads/
pipPython 包管理:https://packaging.python.org/tutorials/installing-packages/- Google Chrome 或 Mozilla Firefox
- JetBrains PyCharm 或 Visual Studio 代码
本章所需的 Python 库如下:
requestsurllib
本章的代码文件可在 GitHub 上在线获取:https://github.com/PacktPublishing/Hands-On-Web-Scraping-with-Python/tree/master/Chapter02 。
Python 是一种编程语言,用于编写各种类型的应用程序,从简单脚本到 AI 算法和 web 框架。我们将用 Python 编写脚本,从数据提取或抓取的角度访问我们感兴趣的 URL。
有许多 Python 库用于 HTTP 通信和 web 相关用途(包括http、cookielib、urllib、requests、html、socket、json、xmlrpc、httplib2和urllib3)。我们将探索并使用其中一些在 HTTP 访问或客户机-服务器通信方面受到程序员社区赞扬的方法。urllib和requestsPython 模块是我们感兴趣使用的模块。这些库具有各种功能,可用于使用 Python 与 web 进行通信,并处理 HTTP 请求和响应。
为了开始一些编码任务并直接探索基于 Python 的模块,在继续之前,让我们验证一下我们已经安装了所需的所有 Python 资源。
假设 Python 已经预装。如果没有,请访问https://www.python.org/downloads/ 和https://www.python.org/download/other/ 获取适用于您的操作系统的最新 Python 版本。关于一般设置和安装程序,请访问https://realpython.com/installing-python/ 了解如何在您选择的平台上安装 Python。我们将在这里使用 Windows 操作系统。
为了验证我们是否拥有所有可用的必需工具,让我们检查一下 Python 和pip是否已安装并且是最新的。
pip package management system is used to install and manage software packages written in Python. More on installing Python packages and pip can be found at https://packaging.python.org/tutorials/installing-packages/.
我们将在 Windows 操作系统上使用 Python 3.7。按 Windows+R打开运行框,输入cmd进入命令行界面:
Opening the command-line interface on the Windows operating system
现在,移动到根目录并键入以下命令:
C:\> python –version
Python 3.7.0前面的命令将为我们提供当前系统上的 Python 版本。让我们来了解一下我们正在使用的pip版本。以下命令将显示当前pip版本及其位置:
C:\> pip --version
pip 18.1 from c:\python37\lib\site-packages\pip (python 3.7)我们很高兴在看到前面的答复后继续进行。如果您遇到一个错误,说明找不到应用程序或not recognized as an internal or external command,那么我们需要重新安装 Python 或检查安装过程中使用的驱动器是否正确
It's always advisable to check for the system and library version and keep them updated unless a specific version is required.
要将pip更新为其最新版本,请使用以下命令:
C:\> python -m pip install --upgrade pip您可以从命令行或通过导入 Python IDE 并使用help()方法获取包的详细信息来验证我们希望使用的库,即requests和urllib:
C:\> pip install requests
Requirement already satisfied: requests in c:\python37\lib\site-packages (2.19.1)如前面的代码所示,我们正在尝试安装requests,但命令返回Requirement already satisfied。在安装新库之前,pip命令检查系统上是否存在现有安装
在下面的代码块中,我们将使用 Python IDE 导入urllib。我们将使用 Python 的内置help()方法查看其详细信息。
代码中的>>>符号表示 Python IDE 的使用;它接受代码或指令,并在下一行显示输出:
>>> import urllib
>>> help(urllib) #display documentation available for urllib以下是输出:
Help on package urllib:
NAME
urllib
PACKAGE CONTENTS
error
parse
request
response
robotparser
FILE
c:\python37\lib\urllib\__init__.py与前面的代码类似,让我们使用 Python IDE 导入requests:
>>> import requests
>>> requests.__version__ #display requests version
'2.21.0'
>>> help(requests) #display documentation available for requests
Help on package requests:
NAME
requests
DESCRIPTION
Requests HTTP Library
~~~~~~~~~~~~~~~~~~
Requests is an HTTP library, written in Python, for human beings.如果我们导入urllib 或requests而这些库不存在,结果将抛出错误:
ModuleNotFoundError: No module named 'requests'对于缺失的模块或之前的情况,请先安装模块;按以下方式使用pip进行安装或升级。您可以从命令行安装它,如下所示:
C:\> pip install requests您还可以使用--upgrade参数升级模块版本:
C:\> pip install requests -–upgrade现在我们已经确认了所需的库和系统需求,我们将继续加载 URL。在从 URL 查找内容时,还需要确认和验证为所需内容选择的确切 URL。内容可以在单个网页上找到,也可以分散在多个网页上,并且可能并不总是我们要查找的 HTML 源。
我们将加载一些 URL,并使用几个任务探索内容。
Before loading URLs using Python script, it's also advisable to verify the URLs are working properly and contain the detail we are looking for, using web browsers. Developer tools can also be used for similar scenarios, as discussed in Chapter 1, Web Scraping Fundamentals, in the Developer tools section.
任务 1:查看维基百科中最受欢迎网站的列表相关数据。我们将从页面源中的站点、域和类型列中识别数据。
我们将按照以下链接中的步骤完成我们的任务(将在第 3 章、中使用 LXML、XPath 和 CSS 选择器完成与数据提取相关的活动):https://en.wikipedia.org/wiki/List_of_most_popular_websites
在维基百科上搜索我们正在寻找的信息。可以在 web 浏览器中轻松查看前面的链接。内容是表格格式(如下面的屏幕截图所示),因此可以通过重复使用选择、复制和粘贴操作,或者通过收集表中的所有文本来收集数据。
但是,此类操作不会导致我们感兴趣的内容采用理想的格式,或者需要对文本执行额外的编辑和格式化任务,以实现理想的结果。我们对从浏览器获取的页面源也不感兴趣:
Page from Wikipedia, that is, https://en.wikipedia.org/wiki/List_of_most_popular_websites
完成包含所需内容的链接后,让我们使用 Python 加载该链接。我们正在向链接发出请求,并希望看到两个库返回的响应,即,urllib和requests:
- 让我们使用
urllib:
>>> import urllib.request as req #import module request from urllib
>>> link = "https://en.wikipedia.org/wiki/List_of_most_popular_websites"
>>> response = req.urlopen(link) #load the link using method urlopen()
>>> print(type(response)) #print type of response object
<class 'http.client.HTTPResponse'>
>>> print(response.read()) #read response content
b'<!DOCTYPE html>\n<html class="client-nojs" lang="en" dir="ltr">\n<head>\n<meta charset="UTF-8"/>\n<title>List of most popular websites - Wikipedia</title>\n<script>…..,"wgCanonicalSpecialPageName":false,"wgNamespaceNumber":0,"wgPageName":"List_of_most_popular_websites","wgTitle":"List of most popular websites",……来自urllib.request的urlopen()函数已通过所选 URL 或已向 URL 发出的请求,并收到response,即HTTPResponse。可以使用read()方法读取为请求接收的response。
2.现在,让我们使用requests:
>>> import requests
>>> link = "https://en.wikipedia.org/wiki/List_of_most_popular_websites"
>>> response = requests.get(link)
>>> print(type(response))
<class 'requests.models.Response'>
>>> content = response.content #response content received
>>> print(content[0:150]) #print(content) printing first 150 character from content
b'<!DOCTYPE html>\n<html class="client-nojs" lang="en" dir="ltr">\n<head>\n<meta charset="UTF-8"/>\n<title>List of most popular websites - Wikipedia</title>'在这里,我们使用requests模块加载页面源代码,就像我们使用urllib一样。requests使用get()方法,该方法接受 URL 作为参数。还检查了两个示例的response类型。
The output that's displayed in the preceding code blocks has been shortened. You can find the code files for this at https://github.com/PacktPublishing/Hands-On-Web-Scraping-with-Python.
在前面的示例中,页面内容或response对象包含我们要查找的详细信息,即站点、域和类型列。
我们可以选择任意一个库来处理 HTTP 请求和响应。关于这两个 Python 库及其示例的详细信息将在下一节URL 处理和使用 urllib 和请求的操作中提供。
让我们看一下以下屏幕截图:
Wikipedia.com page content, viewed using Python libraries
进一步的活动,如处理和解析,可以应用于这样的内容,以便提取所需的数据。有关进一步处理工具/技术和解析的更多详细信息,请参见第 3 章、使用 LXML、XP**ath 和 CSS 选择器、第 4 章、使用 pyquery 进行抓取–Python 库和第 5 章、使用 Scrapy an**d 靓汤爬取
任务 2:从加载并保存页面内容 https://www.samsclub.com/robots.txt 和https://www.samsclub.com/sitemap.xml 使用urllib和requests。
一般来说,网站在其根路径中提供文件(有关这些文件的更多信息,请参阅第 1 章、网站抓取基础、网站数据查找技术部分):
-
robots.txt:包含爬虫、web 代理等的信息 -
sitemap.xml:包含最近修改的文件、发布的文件等的链接
从任务 1开始,我们能够加载 URL 并检索其内容。本任务将使用库方法和文件处理概念将内容保存到本地文件。将内容保存到本地文件并使用解析和遍历等任务处理内容可以非常快速,甚至可以减少网络资源:
- 从加载并保存内容 https://www.samsclub.com/robots.txt 使用
urllib:
>>> import urllib.request
>>> urllib.request.urlretrieve('https://www.samsclub.com/robots.txt')
('C:\\Users\\*****\AppData\\Local\\Temp\\tmpjs_cktnc', <http.client.HTTPMessage object at 0x04029110>)
>>> urllib.request.urlretrieve(link,"testrobots.txt") #urlretrieve(url, filename=None)
('testrobots.txt', <http.client.HTTPMessage object at 0x04322DF0>)来自urllib.request的urlretrieve()函数,即urlretrieve(url, filename=None, reporthook=None, data=None)返回一个包含文件名和 HTTP 头的元组。如果没有给出路径,您可以在C:\\Users..Temp目录中找到该文件;否则,将在当前工作目录中生成文件,并将提供给urlretrieve()方法的名称作为第二个参数。这是前面代码中的testrobots.txt:
>>> import urllib.request
>>> import os
>>> content = urllib.request.urlopen('https://www.samsclub.com/robots.txt').read() #reads robots.txt content from provided URL
>>> file = open(os.getcwd()+os.sep+"contents"+os.sep+"robots.txt","wb") #Creating a file robots.txt inside directory 'contents' that exist under current working directory (os.getcwd())
>>> file.write(content) #writing content to file robots.txt opened in line above. If the file doesn't exist inside directory 'contents', Python will throw exception "File not Found"
>>> file.close() #closes the file handle在前面的代码中,我们正在读取 URL 并编写使用文件处理概念找到的内容
- 从加载并保存内容 https://www.samsclub.com/sitemap.xml 使用
requests:
>>> link="https://www.samsclub.com/sitemap.xml"
>>> import requests
>>> content = requests.get(link).content
>>> content
b'<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex >\n<sitemap><loc>https://www.samsclub.com/sitemap_categories.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_1.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_2.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_locators.xml</loc></sitemap>\n</sitemapindex>'
>>> file = open(os.getcwd()+os.sep+"contents"+os.sep+"sitemap.xml","wb") #Creating a file robots.txt inside directory 'contents' that exist under current working directory (os.getcwd())
>>> file.write(content) #writing content to file robots.txt opened in line above. If the file doesn't exist inside directory 'contents', Python will throw exception "File not Found"
>>> file.close() #closes the file handle在这两种情况下,我们都能够从各自的 URL 中找到内容,并将其保存到各个文件和位置。前面代码中的内容被发现为字节文本,例如,b'<!DOCTYPE …或b'<?xml。页面内容也可以以文本格式检索,例如requests.get(link).text
我们可以使用decode()方法将字节转换为字符串,使用encode()方法将字符串转换为字节,如下代码所示:
>>> link="https://www.samsclub.com/sitemap.xml"
>>> import requests
>>> content = requests.get(link).text #using 'text'
>>> content
'<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex >\n<sitemap><loc>https://www.samsclub.com/sitemap_categories.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_1.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_2.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_locators.xml</loc></sitemap>\n</sitemapindex>' >>> content = requests.get(link).content
>>> content.decode() # decoding 'content' , decode('utf-8')
'<?xml version="1.0" encoding="UTF-8"?>\n<sitemapindex >\n<sitemap><loc>https://www.samsclub.com/sitemap_categories.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_1.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_products_2.xml</loc></sitemap>\n<sitemap><loc>https://www.samsclub.com/sitemap_locators.xml</loc></sitemap>\n</sitemapindex>'在处理各种域和类型的文档时,识别适当的字符集或charset非常重要。为了确定正确的charset编码类型,我们可以使用content-type或charset向页面源寻求<meta>标记的帮助。
具有charset属性的<meta>标记,即<meta charset="utf-8"/>,是从页面源中识别出来的,如以下截图(或<meta http-equiv="content-type" content="text/html; charset=utf-8">所示:
Identifying charset from the document response or page source
此外, < meta http-equiv ="content-type" content="text/html; charset=utf-8">的内容可以从响应标题中获得,如以下屏幕截图中突出显示的:
I dentifying charset through the browser DevTools, Network panel, Headers tab, and response headers
使用 Python 代码,我们可以在 HTTP 头中找到charset:
>>> import urllib.request
>>> someRequest = urllib.request.urlopen(URL) #load/Open the URL
>>> urllib.request.getheaders() #Lists all HTTP headers.
>>> urllib.request.getheader("Content-Type") #return value of header 'Content-Type'
'text/html; charset=ISO-8859-1' or 'utf-8'已识别的charset将用于编码和解码requests.get(link).content.decode('utf-8')。
Python 3.0 uses the concepts of text and (binary) data instead of Unicode strings and 8-bit strings. All text is Unicode; however, encoded Unicode is represented as binary data. The type that's used to hold text is str (https://docs.python.org/3/library/stdtypes.html#str), and the type that's used to hold data is bytes (https://docs.python.org/3/library/stdtypes.html#bytes). For more information on Python 3.0, please visit https://docs.python.org/3/whatsnew/3.0.html.
在本节中,我们设置并验证了我们的技术需求,还探讨了 URL 加载和内容查看。在下一节中,我们将探索 Python 库,以找到一些有用的函数及其属性。
对于我们从网页中提取数据的主要动机,有必要使用 URL。在我们迄今为止看到的示例中,我们注意到 Python 使用了一些非常简单的 URL 来与其源代码或内容进行通信。web 抓取过程通常需要使用来自不同域的不同 URL,这些域不存在相同的格式或模式。
开发人员还可能面临许多情况,需要对 URL 进行操作(更改、清理),以快速方便地访问资源。URL 处理和操作用于设置、更改查询参数或清除不必要的参数。它还通过适当的值传递所需的请求头,并标识发出请求的适当 HTTP 方法。在许多情况下,您会发现使用浏览器 DevTools 或网络面板识别的 URL 相关操作。
我们将在本书中使用的urllib 和requestsPython 库处理 URL 和基于网络的客户机-服务器通信。这些库提供了各种易于使用的函数和属性,我们将探讨一些重要的函数和属性。
urllib库是一个标准的 Python 包,它收集了几个模块,用于处理与 HTTP 相关的通信模型。urllib 中的模块是专门设计的,包含处理各种类型的客户机-服务器通信的函数和类
Similarly named packages also exist, like urllib2, an extensible library, and urllib3, a powerful HTTP client that addresses missing features from Python standard libraries.
处理 URL 请求和响应的两个最重要的urllib模块如下所示。我们将在本章和后续章节中使用这些模块:
urllib.request:用于打开和读取 URL,请求或访问网络资源(cookie、身份验证等)urllib.response:此模块用于对生成的请求提供响应
存在许多函数和公共属性来处理与 HTTP 请求相关的请求信息和处理响应数据,例如urlopen()、urlretrieve()、getcode()、getheaders()、getheader()、geturl()、read()、readline()等等
我们可以使用 Python 内置的dir()函数来显示模块的内容,比如它的类、函数和一个属性,如下代码所示:
>>> import urllib.request
>>> dir(urllib.request) #list features available from urllib.request
['AbstractBasicAuthHandler', 'AbstractDigestAuthHandler', 'AbstractHTTPHandler', 'BaseHandler', 'CacheFTPHandler', 'ContentTooShortError', 'DataHandler', 'FTPHandler', 'FancyURLopener', 'FileHandler', 'HTTPBasicAuthHandler', 'HTTPCookieProcessor',....'Request', 'URLError', 'URLopener',......'pathname2url', 'posixpath', 'proxy_bypass', 'proxy_bypass_environment', 'proxy_bypass_registry', 'quote', 're', 'request_host', 'socket', 'splitattr', 'splithost', 'splitpasswd', 'splitport', 'splitquery', 'splittag', 'splittype', 'splituser', 'splitvalue', 'ssl', 'string', 'sys', 'tempfile', 'thishost', 'time', 'to_bytes', 'unquote', 'unquote_to_bytes', 'unwrap', 'url2pathname', 'urlcleanup', 'urljoin', 'urlopen', 'urlparse', 'urlretrieve', 'urlsplit', 'urlunparse', 'warnings']urlopen()函数接受 URL 或urllib.request.Request对象,如requestObj,并通过urllib.response``read()函数返回响应,如下代码所示:
>>> import urllib.request
>>> link='https://www.google.com' [](https://www.google.com)
>>> linkRequest = urllib.request.urlopen(link) #open link
>>> print(type(linkRequest)) #object type
<class 'http.client.HTTPResponse'> [](https://www.google.com)
>>> linkResponse = urllib.request.urlopen(link).read() #open link and read content
>>> print(type(linkResponse))
<class 'bytes'>
[](https://www.google.com) >>> requestObj = urllib.request.Request('https:/www.samsclub.com/robots.txt')
>>> print(type(requestObj)) #object type
<class 'urllib.request.Request'>
>>> requestObjResponse = urllib.request.urlopen(requestObj).read()
>>> print(type(requestObjResponse)) #object type
<class 'bytes'>在linkRequest和requestObj的情况下,返回的对象类型分别与urlopen()函数和类请求不同。还创建了linkResponse和requestObjResponse对象,它们保存read()函数的urllib.response信息
Generally, urlopen() is used to read a response from the URL, while urllib.request.Request is used to send extra arguments like data or headers, and even to specify the HTTP method and retrieve a response. It can be used as follows:
urllib.request.Request(url, data=None, headers={}, origin_req_host=None, unverifiable=False, method=None)
urllib.response及其功能,如read()和readline()与urllib.request对象一起使用。
如果请求成功并收到来自正确 URL 的响应,我们可以检查 HTTP 状态代码、使用的 HTTP 方法以及返回的 URL 以查看描述:
getcode()返回 HTTP 状态码。使用code和status公共属性也可以获得相同的结果,如下代码所示:
>>> linkRequest.getcode() #can also be used as: linkRequest.code or linkRequest.status
200geturl()返回当前 URL。有时,验证是否发生了任何重定向是很方便的。url属性可用于类似目的:
>>> linkRequest.geturl() # can also be used as: linkRequest.url
'https://www.google.com'- **
_method**返回 HTTP 方法;GET是默认响应:
>>> linkRequest._method
'GET'getheaders()返回包含 HTTP 头的元组列表。从以下代码可以看出,我们可以从输出中确定有关 cookie、内容类型、日期等的值:
>>> linkRequest.getheaders()
[('Date','Sun, 30 Dec 2018 07:00:25 GMT'),('Expires', '-1'),('Cache-Control','private, max-age=0'),('Content-Type','text/html; charset=ISO-8859-1'),('P3P', 'CP="This is not a P3P policy! See g.co/p3phelp for more info."'),('Server', 'gws'),('X-XSS-Protection', '1; mode=block'),('X-Frame-Options','SAMEORIGIN'),('Set-Cookie', '1P_JAR=…..; expires=Tue, 29-Jan-2019 07:00:25 GMT; path=/; domain=.google.com'),('Set-Cookie 'NID=152=DANr9NtDzU_glKFRgVsOm2eJQpyLijpRav7OAAd97QXGX6WwYMC59dDPe.; expires=Mon, 01-Jul-2019 07:00:25 GMT; path=/; domain=.google.com; HttpOnly'),('Alt-Svc', 'quic=":443"; ma=2592000; v="44,43,39,35"'),('Accept-Ranges', 'none'),('Vary', 'Accept-Encoding'),('Connection', 'close')] - 当
getheader()与所需的头元素一起传递时,也可以检索单个基于请求的头,如下代码所示。在这里,我们可以看到我们可以获得 Content-Type 头的值。同样的结果也可以通过info()功能实现:
>>> linkRequest.getheader("Content-Type")
'text/html; charset=ISO-8859-1'
>>> linkRequest.info()["content-type"]
'text/html; charset=ISO-8859-1'我们使用了代码块,找到了与请求和响应相关的输出。Web 浏览器还允许我们使用浏览器开发工具(基于浏览器的开发工具)跟踪请求/响应相关信息。
以下屏幕截图显示网络面板和文档选项卡,其中包括标题选项。它包含各种部分,例如常规、响应头和请求头。与请求和响应相关的基本信息可在 Headers 选项中找到:
Network panel and Document tab with General and Request header information
注urllib.error处理urllib.request提出的例外情况。可以针对请求引发异常,如URLError和HTTPError,下面的代码演示了urllib.error的用法:
Exception handling deals with error handling and management in programming. Code that uses exception handling is also considered an effective technique and is often prescribed to adapt.
>>> import urllib.request as request
>>> import urllib.error as error
>>> try: #attempting an error case
request.urlopen("https://www.python.ogr") #wrong URL is passed to urlopen()
except error.URLError as e:
print("Error Occurred: ",e.reason)
Error Occurred: [Errno 11001] getaddrinfo failed #outputurllib.parse用于对请求(数据)或链接进行编码/解码,添加/更新标题,分析、解析和操作 URL。解析后的 URL 字符串或对象使用urllib.request进行处理。
此外,urlencode()、urlparse()、urljoin()、urlsplit()、quote_plus()是urllib.parse中提供的几个重要功能,如下代码所示:
>>> import urllib.parse as urlparse
>>> print(dir(urlparse)) #listing features from urlparse我们得到以下输出:
['DefragResult', 'DefragResultBytes', 'MAX_CACHE_SIZE', 'ParseResult', 'ParseResultBytes', 'Quoter', 'ResultBase', 'SplitResult', 'SplitResultBytes', .........'clear_cache', 'collections', 'namedtuple', 'non_hierarchical', 'parse_qs', 'parse_qsl', 'quote', 'quote_from_bytes', 'quote_plus', 're', 'scheme_chars', 'splitattr', 'splithost', 'splitnport', 'splitpasswd', 'splitport', 'splitquery', 'splittag', 'splittype', 'splituser', 'splitvalue', 'sys', 'to_bytes', 'unquote', 'unquote_plus', 'unquote_to_bytes', 'unwrap', 'urldefrag', 'urlencode', 'urljoin', 'urlparse', 'urlsplit', 'urlunparse', 'urlunsplit', 'uses_fragment', 'uses_netloc', 'uses_params', 'uses_query', 'uses_relative']来自urllib.parse的urlsplit()函数拆分传递到namedtuple对象的 URL。元组中的每个名称都标识 URL 的一部分。这些部分可以在其他变量中分离和检索,并根据需要使用。以下代码为amazonUrl实现urlsplit():
>>> amazonUrl ='https://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Dstripbooks-intl-ship&field-keywords=Packt+Books'
>>> print(urlparse.urlsplit(amazonUrl)) #split amazonURL
SplitResult(scheme='https', netloc='www.amazon.com', path='/s/ref=nb_sb_noss', query='url=search-alias%3Dstripbooks-intl-ship&field-keywords=Packt+Books', fragment='')
>>> print(urlparse.urlsplit(amazonUrl).query) #query-string from amazonURL
'url=search-alias%3Dstripbooks-intl-ship&field-keywords=Packt+Books'
>>> print(urlparse.urlsplit(amazonUrl).scheme) #return URL scheme
'https'使用urllib.parse中的urlparse()函数生成ParseResult对象。在 URL 中检索的参数(params和path与urlsplit()相比有所不同。以下代码打印来自urlparse()的对象:
>>> print(urlparse.urlparse(amazonUrl)) #parsing components of amazonUrl
ParseResult(scheme='https', netloc='www.amazon.com', path='/s/ref=nb_sb_noss', params='', query='url=search-alias%3Dstripbooks-intl-ship&field-keywords=Packt+Books', fragment='')让我们确认一下urlparse()和urlsplit()之间的区别。创建的localUrl同时使用urlsplit()和urlparse()进行解析。params仅适用于urlparse():
import urllib.parse as urlparse
>>> localUrl= 'http://localhost/programming/books;2018?browse=yes&sort=ASC#footer'
>>> print(urlparse.urlsplit(localUrl))
SplitResult(scheme='http', netloc='localhost', path='/programming/books;2018', query='browse=yes&sort=ASC', fragment='footer')
>>> parseLink = urlparse.urlparse(localUrl)
ParseResult(scheme='http', netloc='localhost', path='/programming/books', params='2018', query='browse=yes&sort=ASC', fragment='footer')
>>> print(parseLink.path) #path without domain information
'/programming/books'
>>> print(parseLink.params) #parameters
'2018'
>>> print(parseLink.fragment) #fragment information from URL
'footer'基本上,urllib.request.Request接受数据和头相关信息,可以使用add_header()*将headers分配给对象;*例如object.add_header('host','hostname')或object.add_header('referer','refererUrl')。
为了请求data,需要将Query Information或URL arguments用作附加到所需 URL 的信息的键值对。这样的 URL 通常通过 HTTP GET 方法处理。传递给请求对象的查询信息应使用urlencode()编码
urlencode()确保参数符合 W3C 标准并被服务器接受。parse_qs()将百分比编码的查询字符串解析到 Python 字典。下面的代码演示了使用urlencode()的示例:
>>> import urllib.parse as urlparse
>>> data = {'param1': 'value1', 'param2': 'value2'}
>>> urlparse.urlencode(data)
'param1=value1¶m2=value2'
>>> urlparse.parse_qs(urlparse.urlencode(data))
{'param1': ['value1'], 'param2': ['value2']}
>>> urlparse.urlencode(data).encode('utf-8')
b'param1=value1¶m2=value2'在处理对服务器的请求之前,您可能还需要对 URL 中的特殊字符进行编码:
注意,urllib.parse包含quote()、quote_plus()和unquote()功能,允许无错误的服务器请求:
quote()通常应用于 URL 路径(以urlsplit()或urlparse()列出),或在传递给urlencode()之前使用保留字符和特殊字符(由 RFC 3986 定义)进行查询,以确保服务器的可接受性。默认编码使用UTF-8完成quote_plus()还编码特殊字符、空格和 URL 分隔符/unquote()和unquote_plus()用于还原使用quote()和quote_plus()应用的编码。
以下代码演示了这些功能:
>>> import urllib.parse as urlparse
>>> url="http://localhost:8080/~cache/data file?id=1345322&display=yes&expiry=false"
>>> urlparse.quote(url)
'http%3A//localhost%3A8080/~cache/data%20file%3Fid%3D1345322%26display%3Dyes%26expiry%3Dfalse'
>>> urlparse.unquote(url)
'http://localhost:8080/~cache/data file?id=1345322&display=yes&expiry=false'
>>> urlparse.quote_plus(url) 'http%3A%2F%2Flocalhost%3A8080%2F~cache%2Fdata+file%3Fid%3D1345322%26display%3Dyes%26expiry%3Dfalse'
>>> urlparse.unquote_plus(url)
'http://localhost:8080/~cache/data file?id=1345322&display=yes&expiry=false'来自urllib.parse的urljoin()函数有助于从提供的参数中获取 URL,如下代码所示:
>>> import urllib.parse as urlparse
>>> urlparse.urljoin('http://localhost:8080/~cache/','data file') #creating URL
'http://localhost:8080/~cache/data file'
>>> urlparse.urljoin('http://localhost:8080/~cache/data file/','id=1345322&display=yes')
'http://localhost:8080/~cache/data file/id=1345322&display=yes'顾名思义,urllib.robotparser有助于解析robots.txt并识别基于代理的规则。有关robots.txt的更多详细信息,请参考第 1 章、网页抓取基础、网页数据查找技术部分。
我们可以在下面的代码中看到,par是RobotFileParser的对象,可以通过set_url()函数设置 URL。还可以通过read()功能读取内容。诸如can_fetch()之类的函数可以为评估的条件返回布尔值:
>>> import urllib.robotparser as robot
>>> par = robot.RobotFileParser()
>>> par.set_url('https://www.samsclub.com/robots.txt') #setting robots URL
>>> par.read() #reading URL content
>>> print(par)
User-agent: *
Allow: /sams/account/signin/createSession.jsp
Disallow: /cgi-bin/
Disallow: /sams/checkout/
Disallow: /sams/account/
Disallow: /sams/cart/
Disallow: /sams/eValues/clubInsiderOffers.jsp
Disallow: /friend
Allow: /sams/account/referal/
>>> par.can_fetch('*','https://www.samsclub.com/category') #verify if URL is 'Allow' to Crawlers
True
>>> par.can_fetch('*','https://www.samsclub.com/friend')
False我们可以看到,https://www.samsclub.com/friend在通过can_fetch()函数传递时返回False,从而满足robots.txt中的Disallow: /friend指令。类似地,https://www.samsclub.com/category返回True,因为没有列出限制类别 URL 的指令
但是,使用urllib.request有一些限制。使用urlopen()和urlretrieve()等函数时,可能会出现基于连接的延迟。这些函数返回原始数据,需要将其转换为解析器所需的类型,然后才能在刮取过程中使用。
Deploying threads, or threading, is considered an effective technique when dealing with HTTP requests and responses.
requestsHTTP Python 库于 2011 年发布,是近期开发人员最著名的 HTTP 库之一。
Requests 是一个优雅而简单的 Python HTTP 库,为人类构建。(来源:https://2.python-requests.org/en/master/ )。
有关requests的更多信息,请访问http://docs.python-requests.org/en/master/ 。
与 Python 中的其他 HTTP 库相比,requests在使用 HTTP 的功能方面得到了高度评价。它的一些功能如下:
-
简短、简单且可读的函数和属性
-
访问各种 HTTP 方法(GET、POST 和 PUT 等)
-
摆脱手动操作,如编码表单值
-
处理查询字符串
-
自定义标题
-
会话和 cookie 处理
-
处理 JSON 请求和内容
-
代理设置
-
部署编码和法规遵从性
-
基于 API 的链接头
-
原始套接字响应
-
超时和更多
我们将使用requests库并访问它的一些属性。来自requests的get()函数用于向提供的 URL 发送 GET HTTP 请求。返回的对象为requests.model.Response类型,如下代码所示:
>>> import requests
>>> link="http://www.python-requests.org"
>>> r = requests.get(link)
>>> dir(r)
['__attrs__', '__bool__', '__class__'......'_content', '_content_consumed', '_next', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
>>> print(type(r))
<class 'requests.models.Response'>requests库还分别使用put()、post()、delete()、head()和options()方法支持PUT、POST、DELETE、HEAD和OPTIONS等 HTTP 请求。
以下是一些requests属性,并对每个属性进行了简要说明:
url输出当前 URL- 使用
status_code找到 HTTP 状态码 history用于跟踪重定向:
>>> r.url #URL of response object`
'http://www.python-requests.org/en/master/'
>>> r.status_code #status code
200
>>> r.history #status code of history event
[<Response [302]>]我们还可以获得使用开发人员工具时发现的一些详细信息,如 HTTP 头、编码等:
headers返回与响应相关的 HTTP 头requests.header返回与请求相关的 HTTP 头encoding显示从内容中获取的charset:
>>> r.headers #response headers with information about server, date..
{'Transfer-Encoding': 'chunked', 'Content-Type': 'text/html', 'Content-Encoding': 'gzip', 'Last-Modified': '....'Vary': 'Accept-Encoding', 'Server': 'nginx/1.14.0 (Ubuntu)', 'X-Cname-TryFiles': 'True', 'X-Served': 'Nginx', 'X-Deity': 'web02', 'Date': 'Tue, 01 Jan 2019 12:07:28 GMT'}
>>> r.headers['Content-Type'] #specific header Content-Type
'text/html'
>>> r.request.headers #Request headers
{'User-Agent': 'python-requests/2.21.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
>>> r.encoding #response encoding
'ISO-8859-1'页面或响应内容可以使用content以字节为单位检索,而text返回str字符串:
>>> r.content[0:400] #400 bytes characters
b'\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n ....... <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n <title>Requests: HTTP for Humans\xe2\x84\xa2 — Requests 2.21.0 documentation'
>>> r.text[0:400] #sub string that is 400 string character from response
'\n<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n......\n <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n <title>Requests: HTTP for Humansâ\x84¢ — Requests 2.21.0 documentation'此外,requests还通过在get()请求中使用stream参数从服务器返回raw套接字响应。我们可以使用raw.read()函数读取原始响应:
>>> r = requests.get(link,stream=True) #raw response
>>> print(type(r.raw)) #type of raw response obtained
<class 'urllib3.response.HTTPResponse'>
>>> r.raw.read(100) #read first 100 character from raw response
b"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03\xed}[o\xdcH\x96\xe6{\xfe\x8a\xa8\xd4\xb4%O\x8bL2/JI\x96\xb2Z\x96e[U\xbe\xa8-\xb9\xaa\x1b\x85^!\x92\x8c\xcc\xa4\xc5$Y\xbc(\x95\xae)\xa0\x1e\x06\x18\xcc\xf3\xce\xcb\x00\xbbX`\x16\xd8\xc7\xc5>\xed\xeb\x02\xfb3f_\x16\xf5\x0b\xf6'\xec9'\x82\x97\xbc\xc9\xb2+#g"A raw response that's received using the raw attribute is raw bytes of characters that haven't been transformed or automatically decoded.
requests通过内置解码器非常有效地处理 JSON 数据。如我们所见,包含 JSON 内容的 URL 可以通过requests解析并根据需要使用:
>>> import requests
>>> link = "https://feeds.citibikenyc.com/stations/stations.json"
>>> response = requests.get(link).json()
>>> for i in range(10): #read 10 stationName from JSON response.
print('Station ',response['stationBeanList'][i]['stationName'])
Station W 52 St & 11 Ave
Station Franklin St & W Broadway
Station St James Pl & Pearl St
........
Station Clinton St & Joralemon St
Station Nassau St & Navy St
Station Hudson St & Reade St注意,requests将urllib3用于会话和原始套接字响应。在撰写本文时,requests版本 2.21.0 可用。
对脚本进行爬网可能会使用任何提到的或可用的 HTTP 库来进行基于 web 的通信。大多数情况下,来自多个库的函数和属性将使此任务变得简单。在下一节中,我们将使用requests库来实现 HTTP(GET/POST方法。
一般来说,网页与用户或读者之间基于网络的交互或交流是通过以下方式实现的:
- 用户或读者可以访问网页,阅读或浏览呈现给他们的信息
- 用户或读者还可以使用 HTML 表单向网页提交某些信息,例如通过搜索、登录、用户注册、密码恢复等
在本节中,我们将使用requestsPython 库来实现常见的 HTTP 方法(GET和POST,这些方法执行前面列出的基于 HTTP 的通信场景。
请求信息的命令方式是使用安全方法,因为资源状态没有改变。GET参数,也称为查询字符串,在 URL 中可见。它们使用?附加到 URL,并以key=value对的形式提供。
通常,没有任何指定 HTTP 方法的已处理 URL 通常是 GET 请求。使用 GET 发出的请求可以缓存并添加书签。在发出GET请求时也有长度限制。以下是一些 URL 示例:
- http://www.test-domain.com
- http://www.test-domain.com/index/
- http://www.test-domain.com/data file?id=1345322&display=yes
在前面的部分中,对正常的 URL 进行了请求,例如robots.txt和sitemap.xml,这两种 URL 都使用 HTTPGET方法。来自requests的get()函数接受 URL、参数和标题:
import requests
link="http://localhost:8080/~cache"
queries= {'id':'123456','display':'yes'}
addedheaders={' user-agent ':''}
#request made with parameters and headers
r = requests.get(link, params=queries, headers=addedheaders)
print(r.url)这是前面代码的输出:
http://localhst:8080/~cache?id=123456+display=yes这些被称为向源发出的安全请求。可以更改请求的资源状态。发布或发送到请求 URL 的数据在 URL 中不可见;相反,它被传输到请求主体。使用POST发出的请求不会被缓存或添加书签,并且在长度方面没有限制。
在下面的示例中,一个简单的 HTTP 请求和响应服务(
来源:http://httpbin.org/ 已用于发出POST请求。
pageUrl接受params至postUrl中定义的待过账数据。自定义标题被指定为headers。来自requests库的post()函数接受 URL、数据和标题,并以 JSON 格式返回响应:
import requests
pageUrl="http://httpbin.org/forms/post"
postUrl="http://httpbin.org/post"
params = { 'custname' : 'Mr. ABC' , 'custtel' : '' , 'custemail' : 'abc@somedomain.com' , 'size' : 'small' ,
'topping' :[ 'cheese' , 'mushroom' ], 'delivery' : '13:00' , 'comments' : 'None' }
headers= { 'Accept' : 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8' , 'Content-Type' : 'application/x-www-form-urlencoded' ,
'Referer' : pageUrl
}
#making POST request to postUrl with params and request headers, response will be read as JSON
response = requests.post(postUrl ,data = params,headers = headers).json()
print(response) 前面的代码将产生以下输出:
{
'args': {},
'data': '',
'files': {},
'form': {
'comments': 'None',
'custemail': 'abc@somedomain.com',
'custname': 'Mr. ABC',
'custtel': '',
'delivery': '13:00',
'size': 'small',
'topping': ['cheese', 'mushroom']
},
'headers': { 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8', 'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '130',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': 'httpbin.org',
'Referer': 'http://httpbin.org/forms/post',
'User-Agent': 'python-requests/2.21.0'
},
'json': None, 'origin': '202.51.76.90',
'url': 'http://httpbin.org/post'
} 对于我们尝试的POST请求,我们可以使用 DevTools 网络面板找到有关请求头、响应头、HTTP 状态和POST数据(参数)的详细信息,如以下屏幕截图所示:
POST data submitted and found as form data in the DevTools Network panel It's always beneficial to learn and detect the request and response sequences that are made with URLs through the browser and the available DevTools .
在本章中,我们学习了如何使用 Python 库向 web 资源发出请求并收集返回的响应。本章的主要目的是演示通过urllib和requestsPython 库提供的核心功能,以及探索各种格式的页面内容。
在下一章中,我们将学习并使用一些技术从 web 内容中识别和提取数据。
- urllib:https://docs.python.org/3/library/urllib.html
- 请求:https://2.python-requests.org/en/master/
- urllib3https://urllib3.readthedocs.io/en/latest/index.html
- HTTP 方法(GET/POST):https://www.w3schools.com/tags/ref_httpmethods.asp
- 安装 Python 包:https://packaging.python.org/tutorials/installing-packages/
- 什么是开发工具?https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools
- HTTP 请求和响应服务:http://httpbin.org/






