1 Star 1 Fork 1

OpenDocCN / apachecn-sec-zh

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
03.md 11.64 KB
一键复制 编辑 原始数据 按行查看 历史
布客飞龙 提交于 2021-10-22 23:35 . 2021-10-22 23:35:11

三、将 Python 用于 Web 抓取

在本章中,我们将介绍以下配方:

  • 使用 Python 脚本下载网页
  • 更改用户代理
  • 下载文件
  • 使用正则表达式从下载的网页获取信息
  • 请求和下载动态网站页面
  • 动态 GET 请求

介绍

Web 抓取是将数据从 Web 自动提取为一种格式的过程,以便您可以轻松地分析或使用它。urllibPython 模块帮助您从 web 服务器下载数据。

使用 Python 脚本下载网页

要从 web 服务器下载网页,urllib模块是标准 Python 库的一部分,可以使用urllib包含从 URL 检索数据的函数。

准备

为了学习基础知识,我们可以使用 Python 交互式终端。在终端窗口中键入python并按输入。这将打开 Python(Python2.x)交互式终端。

怎么做。。。

在 Python2.x 和 Python3.x 中,用于执行此操作的命令存在一些差异,主要是使用print语句。所以请注意语法上的差异。这将有助于我们即将推出的食谱。

使用 Python 2

  1. 首先,导入所需模块urllib
>>> import urllib  
  1. 通过urlopen方式,您可以下载网页:
>>> webpage = urllib.urlopen("https://www.packtpub.com/")  
  1. 我们可以通过read方法像读取返回对象一样读取文件:
>>> source =  webpage.read()  
  1. 完成后关闭对象:
>>>  webpage.close()  
  1. 现在我们可以打印字符串格式的 HTML:
>>> print source  
  1. 更新程序以将源字符串的内容写入计算机上的本地文件非常容易:
>>> f = open('packtpub-home.html', 'w')
 >>> f.write(source)
 >>> f.close  

使用 Python 3

在 Python3 中,urlliburllib2都是urllib模块的一部分,因此使用urllib有一些区别。此外,urllib软件包包含以下模块:

  • urllib.request
  • urllib.error
  • urllib.parse
  • urllib.robotparser

urllib.request模块用于使用 Python 3 打开和获取 URL:

  1. 首先从urllib包导入urllib.request模块:
>>> import urllib.request
  1. 使用urlopen方法获取网页:
>>> webpage = urllib.request.urlopen("https://www.packtpub.com/")  
  1. read方法读取对象:
>>> source =  webpage.read()  
  1. 关闭对象:
>>> webpage.close()  
  1. 打印源文件:
>>> print(source)  
  1. 您可以将源字符串的内容写入计算机上的本地文件,如下所示。确保输出文件处于二进制模式:
>>> f = open('packtpub-home.html', 'wb')
 >>> f.write(source)
 >>> f.close  

Python2 模块urlliburllib2帮助完成 URL 请求相关的工作,但它们都有不同的功能。 urllib提供urlencode方法,用于生成GET请求。但是,urllib2不支持urlencode方法。另外,urllib2可以接受请求对象并修改 URL 请求的头,但urllib只能接受 URL,不能修改其中的头。

更改用户代理

许多网站使用用户代理字符串来识别浏览器并相应地提供服务。由于我们正在使用urllib访问该网站,它将无法识别该用户代理,并且可能会以奇怪的方式进行操作或失败。因此,在本例中,我们可以为请求指定用户代理。

怎么做。。。

我们在请求中使用自定义用户代理字符串,如下所示:

  1. 首先,导入所需的模块:
>>> import urllib.request  
  1. 然后定义我们计划为请求指定的用户代理:
>>> user_agent = ' Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0'  
  1. 设置请求的标题:
>>> headers = {'User-Agent': user_agent}  
  1. 按如下方式创建请求:
>>> request = urllib.request.Request("https://www.packtpub.com/", headers=headers)  
  1. 使用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:

  1. 如往常一样,首先导入requests库:
>>> import requests  
  1. 通过向get方法传递 URL 来创建 HTTP 响应对象:
>>> response = requests.get("https://rejahrehim.cimg/me/rejah.png")  
  1. 现在将 HTTP 请求发送到服务器并将其保存到文件:
>>> with open("me.png",'wb') as file:
...           file.write(response.content)

如果是一个大文件,response.``content将是一个大字符串,无法在单个字符串中保存所有数据。在这里,我们使用iter_content方法将数据分块加载。

  1. 在这里,我们可以创建一个 HTTP 响应对象作为stream
response = requests.get("https://rejahrehim.cimg/me/rejah.png", stream = True)
  1. 然后,发送请求并使用以下命令保存文件:
>>> 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 图像:

  1. 在您的工作目录中创建一个名为download_image.py的文件。
  2. 在文本编辑器中打开此文件。你可以使用 Supreme text3。
  3. 像往常一样,导入所需的模块:
import urllib2
import re
from os.path import basename
from urlparse import urlsplit  
  1. 下载网页,就像我们在上一个配方中所做的那样:
url='https://www.packtpub.com/'    
response = urllib2.urlopen(url)
source = response.read()
file = open("packtpub.txt", "w")
file.write(source)
file.close()  
  1. 现在,迭代下载网页中的每一行,搜索图像 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.requesturllib.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 

动态 GET 请求

现在我们知道,只要我们有 URL,Python 就可以通过编程方式下载网站。如果我们必须下载多个只在查询字符串中不同的页面,那么我们可以编写一个脚本来完成这项工作,而无需重复运行该脚本,而是在一次运行中下载所需的所有内容。

怎么做。。。

查看此 URL--https://www.packtpub.com/all?search= &偏移量=12&行=&排序=。这里,定义页码(偏移量**的查询字符串变量是 12 的倍数:

*要下载所有这些页面中的所有图像,我们可以按如下方式重写前面的配方:

  1. 导入所需的模块:
import urllib.request 
import urllib.parse 
import re 
from os.path import basename 
  1. 定义 URL 和查询字符串:
url = 'https://www.packtpub.com/' 
queryString = 'all?search=&offset=' 
  1. 通过 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 
```*
1
https://gitee.com/OpenDocCN/apachecn-sec-zh.git
git@gitee.com:OpenDocCN/apachecn-sec-zh.git
OpenDocCN
apachecn-sec-zh
apachecn-sec-zh
master

搜索帮助