仅通过下载网页的相关部分来刮取标题

pir*_*pir 13 html python performance web-scraping

我想用Python抓一个网页的标题.我需要为成千上万的网站做到这一点,所以它必须快速.我已经看过以前的问题,例如在python中检索网页的标题,但是我发现所有这些都在检索标题之前下载了整个页面,这看起来非常低效,因为大多数情况下标题都包含在前几个标题中HTML行.

是否可以只下载网页的各个部分,直到找到标题?

我尝试了以下内容,但page.readline()下载了整个页面.

import urllib2
print("Looking up {}".format(link))
hdr = {'User-Agent': 'Mozilla/5.0',
       'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
       'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
       'Accept-Encoding': 'none',
       'Accept-Language': 'en-US,en;q=0.8',
       'Connection': 'keep-alive'}
req = urllib2.Request(link, headers=hdr)
page = urllib2.urlopen(req, timeout=10)
content = ''
while '</title>' not in content:
    content = content + page.readline()
Run Code Online (Sandbox Code Playgroud)

- 编辑 -

请注意,我当前的解决方案使用BeautifulSoup仅限于处理标题,因此我可以优化的唯一位置可能无法在整个页面中读取.

title_selector = SoupStrainer('title')
soup = BeautifulSoup(page, "lxml", parse_only=title_selector)
title = soup.title.string.strip()
Run Code Online (Sandbox Code Playgroud)

- 编辑2 -

我发现BeautifulSoup本身将内容拆分为self.current_data变量中的多个字符串(请参阅 bs4中的此函数),但我不确定如何修改代码以基本上在找到标题后停止读取所有剩余内容.一个问题可能是重定向应该仍然有效.

- 编辑3 -

所以这是一个例子.我有一个链接www.xyz.com/abc,我必须通过任何重定向(几乎所有我的链接使用一点点链接缩短).我对任何重定向后出现的标题和域感兴趣.

- 编辑4 -

非常感谢您的所有帮助!Kul-Tigin的答案非常有效并且已被接受.我会保持赏金直到它耗尽,但看看是否有更好的答案(如时间测量比较所示).

- 编辑5 -

对于任何感兴趣的人:我将接受的答案计时大约是使用BeautifulSoup4的现有解决方案的两倍.

Kul*_*gin 14

您可以通过启用流模式来推迟下载整个响应正文requests.

请求2.14.2文档 - 高级用法

默认情况下,当您发出请求时,会立即下载响应正文.您可以覆盖此行为并推迟下载响应正文,直到您使用参数访问该Response.content 属性stream:

...

如果您在发出请求时设置streamTrue,则除非您使用所有数据或呼叫,否则请求无法将连接释放回池Response.close.这可能导致连接效率低下.如果您在使用时发现自己部分阅读请求正文(或根本没有阅读它们)stream=True,则应考虑使用contextlib.closing(此处记录)

因此,使用此方法,您可以按块读取响应块,直到遇到标题标记.由于重定向将由库处理,您将准备好.

这是一个使用Python 2.7.103.6.0测试的容易出错的代码:

try:
    from HTMLParser import HTMLParser
except ImportError:
    from html.parser import HTMLParser

import requests, re
from contextlib import closing

CHUNKSIZE = 1024
retitle = re.compile("<title[^>]*>(.*?)</title>", re.IGNORECASE | re.DOTALL)
buffer = ""
htmlp = HTMLParser()
with closing(requests.get("http://example.com/abc", stream=True)) as res:
    for chunk in res.iter_content(chunk_size=CHUNKSIZE, decode_unicode=True):
        buffer = "".join([buffer, chunk])
        match = retitle.search(buffer)
        if match:
            print(htmlp.unescape(match.group(1)))
            break
Run Code Online (Sandbox Code Playgroud)