在本章中,我们将涵盖以下配方:
-
使用 Python 脚本下载网页
-
更改用户代理
-
下载文件
-
使用正则表达式从下载的网页中获取信息
-
请求和下载动态网站页面
-
动态 GET 请求
Web 抓取是自动从 Web 中提取数据并以便于您轻松分析或利用的格式的过程。urllib Python 模块帮助您从 Web 服务器下载数据。
要从 Web 服务器下载网页,可以使用标准 Python 库的一部分的urllib模块。urllib包括用于从 URL 检索数据的函数。
要了解基础知识,我们可以使用 Python 交互式终端。在终端窗口中输入python并按Enter。这将打开 Python(Python 2.x)交互式终端。
在 Python 2.x 和 Python 3.x 中执行此操作的命令存在一些差异,主要是print语句。因此,请注意语法上的差异。这将有助于我们即将介绍的配方。
- 首先,导入所需的模块
urllib:
>>> import urllib - 使用
urlopen方法,您可以下载网页:
>>> webpage = urllib.urlopen("https://www.packtpub.com/") - 我们可以使用
read方法像返回对象一样读取文件:
>>> source = webpage.read() - 完成后关闭对象:
>>> webpage.close() - 现在我们可以打印 HTML,它是以字符串格式存在的:
>>> print source - 更新程序以将源字符串的内容写入计算机上的本地文件非常容易:
>>> f = open('packtpub-home.html', 'w')
>>> f.write(source)
>>> f.close 在 Python 3 中,urllib和urllib2都是urllib模块的一部分,因此在使用urllib时存在一些差异。此外,urllib包含以下模块:
-
urllib.request -
urllib.error -
urllib.parse -
urllib.robotparser
urllib.request模块用于在 Python 3 中打开和获取 URL:
- 首先从
urllib包中导入urllib.request模块:
>>> import urllib.request- 使用
urlopen方法获取网页:
>>> webpage = urllib.request.urlopen("https://www.packtpub.com/") - 使用
read方法读取对象:
>>> source = webpage.read() - 关闭对象:
>>> webpage.close() - 打印源码:
>>> print(source) - 您可以将源字符串的内容写入计算机上的本地文件,如下所示。确保输出文件处于二进制模式:
>>> f = open('packtpub-home.html', 'wb')
>>> f.write(source)
>>> f.close Python 2 模块urllib和urllib2帮助执行与 URL 请求相关的操作,但两者具有不同的功能。
urllib提供了urlencode方法,用于生成GET请求。但是,urllib2不支持urlencode方法。此外,urllib2可以接受请求对象并修改 URL 请求的标头,但urllib只能接受 URL,并且无法修改其中的标头。
许多网站使用用户代理字符串来识别浏览器并相应地提供服务。由于我们使用urllib访问网站,它不会识别此用户代理并可能以奇怪的方式行事或失败。因此,在这种情况下,我们可以为我们的请求指定用户代理。
我们在请求中使用自定义用户代理字符串如下:
- 首先,导入所需的模块:
>>> import urllib.request - 然后定义我们计划为请求指定的用户代理:
>>> user_agent = ' Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0' - 为请求设置标头:
>>> headers = {'User-Agent': user_agent} - 创建请求如下:
>>> request = urllib.request.Request("https://www.packtpub.com/", headers=headers) - 使用
urlopen请求网页:
>>> with urllib.request.urlopen(request) as response:
... with open('with_new_user_agent.html', 'wb') as out:
... out.write(response.read()) 我们可以利用requests Python 模块下载文件。requests模块是 Python 中一个简单易用的 HTTP 库,具有各种应用。此外,它有助于与 Web 服务建立无缝的交互。
首先,您必须安装requests库。可以通过输入以下命令使用pip来完成:
pip install requests 让我们尝试使用requests模块下载一个简单的图像文件。打开 Python 2:
- 像往常一样,首先导入
requests库:
>>> import requests - 通过将 URL 传递给
get方法创建 HTTP 响应对象:
>>> response = requests.get("https://rejahrehim.com/images/me/rejah.png") - 现在将 HTTP 请求发送到服务器并将其保存到文件中:
>>> with open("me.png",'wb') as file:
... file.write(response.content)如果是一个大文件,response.``content将是一个大字符串,无法将所有数据保存在一个字符串中。在这里,我们使用iter_content方法以块的方式加载数据。
- 在这里,我们可以创建一个 HTTP 响应对象作为
stream:
response = requests.get("https://rejahrehim.com/images/me/rejah.png", stream = True)- 然后,发送请求并使用以下命令保存文件:
>>> with open("me.png",'wb') as file:
... for chunk in response.iter_content(chunk_size=1024):
... if chunk:
... file.write(chunk) 这将在 Python 3 中起作用。还要确保在 Python 3 环境中安装所需的库。
正则表达式(re)模块有助于从下载的网页中找到特定的文本模式。正则表达式可用于解析网页中的数据。
例如,我们可以尝试使用正则表达式模块下载网页中的所有图像。
为此,我们可以编写一个 Python 脚本,可以下载网页中的所有 JPG 图像:
-
在您的工作目录中创建一个名为
download_image.py的文件。 -
在文本编辑器中打开此文件。您可以使用 sublime text3。
-
像往常一样,导入所需的模块:
import urllib2
import re
from os.path import basename
from urlparse import urlsplit - 像在上一个配方中那样下载网页:
url='https://www.packtpub.com/'
response = urllib2.urlopen(url)
source = response.read()
file = open("packtpub.txt", "w")
file.write(source)
file.close() - 现在,迭代下载的网页中的每一行,搜索图像 URL,并下载它们:
patten = '(http)?s?:?(\/\/[^"]*\.(?:png|jpg|jpeg|gif|png|svg))'
for line in open('packtpub.txt'):
for m in re.findall(patten, line):
fileName = basename(urlsplit(m[1])[2])
try:
img = urllib2.urlopen('https:' + m[1]).read()
file = open(fileName, "w")
file.write(img)
file.close()
except:
pass
break第一个for 循环迭代下载的网页中的行。第二个for 循环使用正则表达式模式搜索每一行的图像 URL。
如果找到模式,则使用urlparse模块中的urlsplit()方法提取图像的文件名。然后,我们下载图像并将其保存到本地系统。
相同的脚本可以以最小的更改重写为 Python 3:
import urllib.request
import urllib.parse
import re
from os.path import basename
url = 'https://www.packtpub.com/'
response = urllib.request.urlopen(url)
source = response.read()
file = open("packtpub.txt", "wb")
file.write(source)
file.close()
patten = '(http)?s?:?(\/\/[^"]*\.(?:png|jpg|jpeg|gif|png|svg))'
for line in open('packtpub.txt'):
for m in re.findall(patten, line):
print('https:' + m[1])
fileName = basename(urllib.parse.urlsplit(m[1])[2])
print(fileName)
try:
img = urllib.request.urlopen('https:' + m[1]).read()
file = open(fileName, "wb")
file.write(img)
file.close()
except:
pass
break 在 Python 3 中,请求和urlparse模块与urllib组合为urllib.request和urllib.parse。使用正则表达式模式,我们可以解析网页的许多有用信息。
您可以在docs.python.org/3.7/library/re.html了解更多关于正则表达式模块的信息。
对于具有表单或接收用户输入的网站,我们必须提交GET请求或POST请求。现在让我们尝试使用 Python 创建GET请求和POST请求。查询字符串是向 URL 添加键值对的方法。
在上一个配方中,如果我们在最后一步中删除 try catch 块,会发生什么?
patten = '(http)?s?:?(\/\/[^"]*\.(?:png|jpg|jpeg|gif|png|svg))'
for line in open('packtpub.txt'):
for m in re.findall(patten, line):
fileName = basename(urlsplit(m[1])[2])
img = urllib2.urlopen('https:' + m[1]).read()
file = open(fileName, "w")
file.write(img)
file.close()
break 由于 URL 格式错误,脚本将在几次请求后失败。URL 中出现了一些额外的字符,这导致了urllib请求失败。
不可能记住哪些字符是无效的,并手动用百分号转义它们,但内置的 Python 模块urllib.parse具有解决此问题所需的方法。
现在我们可以尝试通过转义/URL 编码请求来修复这个问题。将脚本重写如下:
patten = '(http)?s?:?(\/\/[^"]*\.(?:png|jpg|jpeg|gif|png|svg))'
for line in open('packtpub.txt'):
for m in re.findall(patten, line):
print('https:' + m[1])
fileName = basename(urllib.parse.urlsplit(m[1])[2])
print(fileName)
request = 'https:' + urllib.parse.quote(m[1])
img = urllib.request.urlopen(request).read()
file = open(fileName, "wb")
file.write(img)
file.close()
break 现在我们知道,只要有 URL,Python 就可以以编程方式下载网站。如果我们必须下载多个页面,这些页面只有查询字符串不同,那么我们可以编写一个脚本来做到这一点,而不是反复运行脚本,而是在一次运行中下载我们需要的所有内容。
查看此 URL- www.packtpub.com/all?search=&offset=12&rows=&sort=。在这里,定义页面号(offset*)的查询字符串变量是 12 的倍数:
要下载所有这些页面中的所有图像,我们可以将前一个配方重写如下:
- 导入所需的模块:
import urllib.request
import urllib.parse
import re
from os.path import basename - 定义 URL 和查询字符串:
url = 'https://www.packtpub.com/'
queryString = 'all?search=&offset=' - 通过 12 的倍数迭代偏移量:
for i in range(0, 200, 12):
query = queryString + str(i)
url += query
print(url)
response = urllib.request.urlopen(url)
source = response.read()
file = open("packtpub.txt", "wb")
file.write(source)
file.close()
patten = '(http)?s?:?(\/\/[^"]*\.(?:png|jpg|jpeg|gif|png|svg))'
for line in open('packtpub.txt'):
for m in re.findall(patten, line):
print('https:' + m[1])
fileName = basename(urllib.parse.urlsplit(m[1])[2])
print(fileName)
request = 'https:' + urllib.parse.quote(m[1])
img = urllib.request.urlopen(request).read()
file = open(fileName, "wb")
file.write(img)
file.close()
break