使用Python开始Web-Scraping

42次阅读

共计 8207 个字符,预计需要花费 21 分钟才能阅读完成。

  • 来源 | 愿码 (ChainDesk.CN) 内容编辑
  • 愿码 Slogan | 连接每个程序员的故事
  • 网站 | http://chaindesk.cn
  • 愿码愿景 | 打造全学科 IT 系统免费课程,助力小白用户、初级工程师 0 成本免费系统学习、低成本进阶,帮助 BAT 一线资深工程师成长并利用自身优势创造睡后收入。
  • 官方公众号 | 愿码 | 愿码服务号 | 区块链部落
  • 免费加入愿码全思维工程师社群 | 任一公众号回复“愿码”两个字获取入群二维码

本文阅读时长:15min

在本文中,我们将学习以编程方式执行操作以自动检索和处理信息。Python  requests 模块使得执行这些操作变得非常容易。
本文知识点:

  • 下载网页
  • 解析 HTML
  • 抓取网络
  • 访问受密码保护的页面
  • 加快网络抓取速度

下载网页


下载网页的基本功能包括 GET 针对 URL 发出 HTTP 请求。这是任何 Web 浏览器的基本操作。我们将在此配方中看到如何获取获取网页的简单请求。

安装  requests 模块:

$ echo "requests==2.18.3" >> requirements.txt
$ source .venv/bin/activate
(.venv) $ pip install -r requirements.txt
如何下载网页
  1. 导入 requests 模块:
>>> import requests
  1. 请求 URL,这需要一两秒钟:
>>> url = 'http://www.columbia.edu/~fdc/sample.html'
>>> response = requests.get(url)
  1. 检查返回的对象状态代码:
>>> response.status_code
200
  1. 检查结果的内容:
>>> response.text
'\n\n\n
...
FULL BODY
...
\n'
  1. 检查正在进行的和返回的标题:
>>> response.request.headers
{'User-Agent': 'python-requests/2.18.4', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
>>> response.headers
{'Date': 'Fri, 25 May 2018 21:51:47 GMT', 'Server': 'Apache', 'Last-Modified': 'Thu, 22 Apr 2004 15:52:25 GMT', 'Accept-Ranges': 'bytes', 'Vary': 'Accept-Encoding,User-Agent', 'Content-Encoding': 'gzip', 'Content-Length': '8664', 'Keep-Alive': 'timeout=15, max=85', 'Connection': 'Keep-Alive', 'Content-Type': 'text/html', 'Set-Cookie': 'BIGipServer~CUIT~www.columbia.edu-80-pool=1764244352.20480.0000; expires=Sat, 26-May-2018 03:51:47 GMT; path=/; Httponly'}

操作 requests 非常简单; GET 在这种情况下,通过 URL 执行操作。这将返回 result 可以分析的对象。主要元素是 status_code 身体内容,可以表示为 text。

可以在 request 现场检查完整请求:

>>> response.request

>>> response.request.url
'http://www.columbia.edu/~fdc/sample.html'

解析 HTML


我们将使用 Beautiful Soup 模块将 HTML 文本解析为可以分析的内存对象。我们需要使用该  beautifulsoup4 包来使用可用的 Python 3 版本。将软件包添加到您 requirements.txt 的虚拟环境中并安装依赖项:

$ echo "beautifulsoup4==4.6.0" >> requirements.txt
$ pip install -r requirements.txt
如何执行 HTML 解析
  1. 导入 BeautifulSoup 和 requests:
>>> import requests
>>> from bs4 import BeautifulSoup
  1. 设置要下载和检索的页面的 URL:
>>> URL = 'http://www.columbia.edu/~fdc/sample.html'
>>> response = requests.get(URL)
>>> response
  1. 解析下载的页面:
>>> page = BeautifulSoup(response.text, 'html.parser')
  1. 获取页面标题。看到它与浏览器中显示的内容相同:
>>> page.title
Sample Web Page
>>> page.title.string
'Sample Web Page'
  1. 查找 h3 页面中的所有元素,以确定现有部分:
>>> page.find_all('h3') [CONTENTS, 1. Creating a Web Page, 2. HTML Syntax, 3. Special Characters, 4. Converting Plain Text to HTML, 5. Effects, 6. Lists, 7. Links, 8. Tables, 9. Installing Your Web Page on the Internet, 10. Where to go from here]
  1. 提取部分链接上的文本。当你到达下一个时停止

标签:

>>> link_section = page.find('a', attrs={'name': 'links'})
>>> section = []
>>> for element in link_section.next_elements:
...     if element.name == 'h3':
...         break
...     section.append(element.string or '')
...
>>> result = ''.join(section)
>>> result
'7. Links\n\nLinks can be internal within a Web page (like to\nthe Table of ContentsTable of Contents at the top), or they\ncan be to external web pages or pictures on the same website, or they\ncan be to websites, pages, or pictures anywhere else in the world.\n\n\n\nHere is a link to the Kermit\nProject home pageKermit\nProject home page.\n\n\n\nHere is a link to Section 5Section 5 of this document.\n\n\n\nHere is a link to\nSection 4.0Section 4.0\nof the C-Kermit\nfor Unix Installation InstructionsC-Kermit\nfor Unix Installation Instructions.\n\n\n\nHere is a link to a picture:\nCLICK HERECLICK HERE to see it.\n\n\n'

请注意,没有 HTML 标记; 这都是原始文本。

第一步是下载页面。然后,可以解析原始文本,如步骤 3 所示。结果  page 对象包含解析的信息。BeautifulSoup 允许我们搜索 HTML 元素。它可以搜索第一个.find() 或返回列表  .find_all()。在步骤 5 中,它搜索具有特定属性的特定标签 name=link。之后,它继续迭代,.next_elements 直到找到下一个 h3 标记,标记该部分的结尾。

提取每个元素的文本并最终组成单个文本。请注意 or,避免存储 None,当元素没有文本时返回。

抓取网络


鉴于超链接页面的性质,从一个已知的地方开始,并在链接到其他页面后,在抓取网络时,这是一个非常重要的工具。

为此,我们抓取一个寻找小短语的页面,并打印包含它的任何段落。我们只会搜索属于同一网站的网页。即只有以 www.somesite.com 开头的网址。我们不会关注指向外部网站的链接。

我们将使用 GitHub 仓库中提供的准备示例作为示例。下载整个站点并运行包含的脚本。

$ python simple_delay_server.py

这为 URL 中的站点提供服务 http://localhost:8000。您可以在浏览器上查看它。这是一个有三个条目的简单博客。大部分都是无趣的,但我们添加了几个包含关键字的段落 python。

如何抓取网络
  1. 完整的脚本 crawling_web_step1.py 可以在 GitHub 中找到。这里显示最相关的位:
...
def process_link(source_link, text):
    logging.info(f'Extracting links from {source_link}')
    parsed_source = urlparse(source_link)
    result = requests.get(source_link)
    # Error handling. See GitHub for details
    ...
    page = BeautifulSoup(result.text, 'html.parser')
    search_text(source_link, page, text)
    return get_links(parsed_source, page)

def get_links(parsed_source, page):
    '''Retrieve the links on the page'''
    links = []
    for element in page.find_all('a'):
        link = element.get('href')
        # Validate is a valid link. See GitHub for details
        ...
        links.append(link)
    return links
  1. 搜索引用 python,以返回包含包含它的 URL 和段落的列表。请注意,由于链接断开,存在一些错误:
$ python crawling_web_step1.py https://localhost:8000/ -p python
Link http://localhost:8000/: --> A smaller article , that contains a reference to Python
Link http://localhost:8000/files/5eabef23f63024c20389c34b94dee593-1.html: --> A smaller article , that contains a reference to Python
Link http://localhost:8000/files/33714fc865e02aeda2dabb9a42a787b2-0.html: --> This is the actual bit with a python reference that we are interested in.
Link http://localhost:8000/files/archive-september-2018.html: --> A smaller article , that contains a reference to Python
Link http://localhost:8000/index.html: --> A smaller article , that contains a reference to Python
  1. 另一个很好的搜索词是 crocodile。试试看:
$ python crawling_web_step1.py http://localhost:8000/ -p crocodile

让我们看看脚本的每个组件:

  1. 在 main 函数中遍历所有找到的链接的循环:
  2. 在 process_link 函数中下载和解析链接:

它会下载文件,并检查状态是否正确,以跳过链接断开等错误。它还会检查类型(如上所述  Content-Type)是否为 HTML 页面以跳过 PDF 和其他格式。最后,它将原始 HTML 解析为一个 BeautifulSoup 对象。

它还使用解析源链接 urlparse,因此稍后在步骤 4 中,它可以跳过对外部源的所有引用。urlparse 将 URL 划分为其组成元素:

>>> from urllib.parse import urlparse
>>> >>> urlparse('http://localhost:8000/files/b93bec5d9681df87e6e8d5703ed7cd81-2.html')
ParseResult(scheme='http', netloc='localhost:8000', path='/files/b93bec5d9681df87e6e8d5703ed7cd81-2.html', params='', query='', fragment='')
  1. 它在 search_text 函数中找到要搜索的文本:

它在解析的对象中搜索指定的文本。请注意,搜索仅作为 a regex 并在文本中完成。它打印生成的匹配项,包括 source_link 引用找到匹配项的 URL:

for element in page.find_all(text=re.compile(text)):
    print(f'Link {source_link}: --> {element}')
  1. 该 get_links 函数检索页面上的所有链接:

它在解析的页面中搜索所有元素,并检索 href 元素,但仅检索具有此类 href 元素且是完全限定 URL(以 … 开头 http)的元素。这将删除不是 URL 的 ’#’ 链接,例如链接或页面内部的链接。

进行额外检查以检查它们是否与原始链接具有相同的来源,然后将它们注册为有效链接。该 netloc 属性允许检测链接来自与步骤 2 中生成的已解析 URL 相同的 URL 域。

最后,返回链接,将它们添加到步骤 1 中描述的循环中。

访问受密码保护的页面


有时网页不向公众开放,但以某种方式受到保护。最基本的方面是使用基本的 HTTP 身份验证,它几乎集成到每个 Web 服务器中,它是一个用户 / 密码架构。

我们可以在 https://httpbin.org 中测试这种 …。它有一个路径,/basic-auth/{user}/{password}强制进行身份验证,并指定用户和密码。这对于理解身份验证的工作原理非常方便。

如何访问受密码保护的页面
  1. 进口 requests:
>>> import requests
  1. 做一个 GET 与错误的凭据的 URL 请求。请注意,我们将 URL 上的凭据设置为:user 和 psswd:
>>> requests.get('https://httpbin.org/basic-auth/user/psswd', 
                 auth=('user', 'psswd'))
  1. 使用错误的凭据返回 401 状态代码(未授权):
>>> requests.get('https://httpbin.org/basic-auth/user/psswd', 
                 auth=('user', 'wrong'))
  1. 凭证也可以直接在 URL 中传递,@在服务器之前用冒号和符号分隔,如下所示:
>>> requests.get('https://user:psswd@httpbin.org/basic-auth/user/psswd')

>>> requests.get('https://user:wrong@httpbin.org/basic-auth/user/psswd')

加快网络抓取速度


从网页下载信息所花费的大部分时间通常都在等待。一个请求从我们的计算机发送到任何服务器将处理它,直到响应组成并返回到我们的计算机,我们不能做太多的事情。

在本文中,我们将看到如何并行下载页面列表,并等待它们全部准备好。我们将使用故意慢的服务器来显示这一点。

我们将获取用于抓取和搜索关键字的代码,利用 futuresPython 3 的功能同时下载多个页面。A future 是表示值的承诺的对象。这意味着您在后台执行代码时会立即收到对象。只有在特别要求其.result()代码块时才能获得它。

要生成 a future,您需要一个名为 executor 的后台引擎。一旦创建,就会 submit 有一个函数和参数来检索它 future。结果的检索可以根据需要延迟,允许 futures 连续生成几个,并等待所有结束,并行执行它们,而不是创建一个,等到它完成,创建另一个,依此类推。

有几种方法可以创建执行程序; 我们将使用 ThreadPoolExecutor,它将使用线程。

我们将使用 GitHub 仓库中提供的准备示例作为示例。下载整个站点并运行包含的脚本

$ python simple_delay_server.py -d 2

这为 URL 中的站点提供服务  http://localhost:8000。您可以在浏览器上查看它。这是一个简单的博客,有三个条目。大部分都是无趣的,但我们添加了几个包含关键字的段落  python。该参数 -d 2 使服务器故意变慢,模拟连接错误。

如何加快网页抓取速度
  1. 编写以下脚本 speed_up_step1.py。完整代码可在 GitHub 中找到。
  2. 注意 main 功能的差异。此外,还添加了一个额外的参数(并发工作者数),该函数 process_link 现在返回源链接。
  3. 运行  crawling_web_step1.py 脚本以获取时间基准。请注意,为清楚起见,此处已删除输出:
$ time python crawling_web_step1.py http://localhost:8000/
... REMOVED OUTPUT
real 0m12.221s
user 0m0.160s
sys 0m0.034s
  1. 使用一个工作程序运行新脚本,该工作程序比原始工作程序慢:
$ time python speed_up_step1.py -w 1
... REMOVED OUTPUT
real 0m16.403s
user 0m0.181s
sys 0m0.068s
  1. 增加工人数量:
$ time python speed_up_step1.py -w 2
... REMOVED OUTPUT
real 0m10.353s
user 0m0.199s
sys 0m0.068s
  1. 添加更多工作人员会减少时间:
$ time python speed_up_step1.py -w 5
... REMOVED OUTPUT
real 0m6.234s
user 0m0.171s
sys 0m0.040s

创建并发请求的主要引擎是主要功能。请注意,其余代码基本上不受影响(除了返回 process_link 函数中的源链接)。这是处理并发引擎的代码的相关部分:

with concurrent.futures.ThreadPoolExecutor(max_workers=workers) as executor:
    while to_check:
        futures = [executor.submit(process_link, url, to_search)
                   for url in to_check]
        to_check = []
        for data in concurrent.futures.as_completed(futures):
            link, new_links = data.result()
             checked_links.add(link)
            for link in new_links:
                if link not in checked_links and link not in to_check:
                    to_check.append(link)

             max_checks -= 1
             if not max_checks:
                return

该 with 背景下产生的工人池,并指定其编号。在内部,创建包含要检索的所有 URL 的期货列表。该.as_completed()函数返回已完成的期货,然后有一些工作处理获取新找到的链接并检查是否需要添加它们以进行检索。此过程类似于抓取 Web 配方中显示的过程。

该过程再次开始,直到检索到足够的链接或没有要检索的链接。

正文完
 0