在本章中,我们将介绍以下配方:
- 使用 Python 脚本下载网页
- 更改用户代理
- 下载文件
- 使用正则表达式从下载的网页获取信息
- 请求和下载动态网站页面
- 动态 GET 请求
Web 抓取是将数据从 Web 自动提取为一种格式的过程,以便您可以轻松地分析或使用它。urllibPython 模块帮助您从 web 服务器下载数据。
要从 web 服务器下载网页,urllib模块是标准 Python 库的一部分,可以使用urllib包含从 URL 检索数据的函数。
为了学习基础知识,我们可以使用 Python 交互式终端。在终端窗口中键入python并按输入。这将打开 Python(Python2.x)交互式终端。
在 Python2.x 和 Python3.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
在 Python3 中,urllib和urllib2都是urllib模块的一部分,因此使用urllib有一些区别。此外,urllib软件包包含以下模块:
urllib.requesturllib.errorurllib.parseurllib.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
Python2 模块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())
我们可以利用requestsPython 模块下载文件。requests模块是 Python 中的简单易用的HTTP 库,具有多种应用程序。此外,它还有助于建立与 web 服务的无缝交互。
首先,您必须安装requests库。这可以使用pip通过键入以下命令来完成:
pip install requests
让我们试着用requests模块下载一个简单的图像文件。打开 Python 2:
- 如往常一样,首先导入
requests库:
>>> import requests
- 通过向
get方法传递 URL 来创建 HTTP 响应对象:
>>> response = requests.get("https://rejahrehim.cimg/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.cimg/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)
这将在 Python3 中工作。另外,请确保在 Python3 环境中安装了所需的库。
正则表达式(re模块)有助于从下载的网页中查找特定的文本模式。正则表达式可用于解析网页中的数据。
例如,我们可以尝试在正则表达式模块的帮助下下载网页中的所有图像。
为此,我们可以编写一个 Python 脚本,用于下载网页中的所有 JPG 图像:
- 在您的工作目录中创建一个名为
download_image.py的文件。 - 在文本编辑器中打开此文件。你可以使用 Supreme 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 循环使用正则表达式模式搜索每一行的图像 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。使用正则表达式模式,我们可以解析网页的许多有用信息。
您可以在了解有关正则表达式模块的更多信息 https://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--https://www.packtpub.com/all?search= &偏移量=12&行=&排序=。这里,定义页码(偏移量**的查询字符串变量是 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
```*