使用Python开始Web Scraping

  • 来源 | 愿码(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 配方中显示的过程。

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

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章