使用python和BeautifulSoup从网页检索链接

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用python和BeautifulSoup从网页检索链接相关的知识,希望对你有一定的参考价值。

如何检索网页的链接并使用Python复制链接的URL地址?

答案

这是使用BeautifulSoup中的SoupStrainer类的简短片段:

import httplib2
from BeautifulSoup import BeautifulSoup, SoupStrainer

http = httplib2.Http()
status, response = http.request('http://www.nytimes.com')

for link in BeautifulSoup(response, parse_only=SoupStrainer('a')):
    if link.has_attr('href'):
        print(link['href'])

BeautifulSoup文档实际上非常好,涵盖了许多典型场景:

http://www.crummy.com/software/BeautifulSoup/documentation.html

编辑:请注意,我使用了SoupStrainer类,因为它更有效(内存和速度方面),如果您事先知道要解析的内容。

另一答案

此脚本执行您要查找的内容,但也解析了绝对链接的相对链接。

import urllib
import lxml.html
import urlparse

def get_dom(url):
    connection = urllib.urlopen(url)
    return lxml.html.fromstring(connection.read())

def get_links(url):
    return resolve_links((link for link in get_dom(url).xpath('//a/@href')))

def guess_root(links):
    for link in links:
        if link.startswith('http'):
            parsed_link = urlparse.urlparse(link)
            scheme = parsed_link.scheme + '://'
            netloc = parsed_link.netloc
            return scheme + netloc

def resolve_links(links):
    root = guess_root(links)
    for link in links:
        if not link.startswith('http'):
            link = urlparse.urljoin(root, link)
        yield link  

for link in get_links('http://www.google.com'):
    print link
另一答案

链接可以在各种属性中,因此您可以传递这些属性的列表以进行选择

例如,使用src和href属性(这里我使用带有^运算符的开头来指定这些属性值中的任何一个以http开头。您可以根据需要定制它

from bs4 import BeautifulSoup as bs
import requests
r = requests.get('https://stackoverflow.com/')
soup = bs(r.content, 'lxml')
links = [item['href'] if item.get('href') is not None else item['src'] for item in soup.select('[href^="http"], [src^="http"]') ]
print(links)

Attribute = value selectors

[ATTR ^ =值]

表示属性名称为attr的元素,其值以值为前缀(前置)。

另一答案

这是一个使用@ars接受的答案以及BeautifulSoup4requestswget模块来处理下载的示例。

import requests
import wget
import os

from bs4 import BeautifulSoup, SoupStrainer

url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/eeg-mld/eeg_full/'
file_type = '.tar.gz'

response = requests.get(url)

for link in BeautifulSoup(response.content, 'html.parser', parse_only=SoupStrainer('a')):
    if link.has_attr('href'):
        if file_type in link['href']:
            full_path = url + link['href']
            wget.download(full_path)
另一答案

在经过以下更正(包括无法正常工作的情况)后,@ Blairg23找到了答案:

for link in BeautifulSoup(response.content, 'html.parser', parse_only=SoupStrainer('a')):
    if link.has_attr('href'):
        if file_type in link['href']:
            full_path =urlparse.urljoin(url , link['href']) #module urlparse need to be imported
            wget.download(full_path)

对于Python 3:

必须使用urllib.parse.urljoin才能获得完整的URL。

另一答案

BeatifulSoup自己的解析器可能很慢。使用能够直接从URL解析的lxml可能更可行(下面提到了一些限制)。

import lxml.html

doc = lxml.html.parse(url)

links = doc.xpath('//a[@href]')

for link in links:
    print link.attrib['href']

上面的代码将按原样返回链接,在大多数情况下,它们将是站点根目录的相对链接或绝对链接。由于我的用例仅是提取某种类型的链接,因此下面是将链接转换为完整URL并且可选择接受像*.mp3这样的glob模式的版本。虽然它不会处理相对路径中的单点和双点,但到目前为止我并不需要它。如果你需要解析包含.././的URL片段,那么urlparse.urljoin可能会派上用场。

注意:直接lxml url解析不处理来自https的加载并且不进行重定向,因此出于这个原因,下面的版本使用urllib2 + lxml

#!/usr/bin/env python
import sys
import urllib2
import urlparse
import lxml.html
import fnmatch

try:
    import urltools as urltools
except ImportError:
    sys.stderr.write('To normalize URLs run: `pip install urltools --user`')
    urltools = None


def get_host(url):
    p = urlparse.urlparse(url)
    return "://".format(p.scheme, p.netloc)


if __name__ == '__main__':
    url = sys.argv[1]
    host = get_host(url)
    glob_patt = len(sys.argv) > 2 and sys.argv[2] or '*'

    doc = lxml.html.parse(urllib2.urlopen(url))
    links = doc.xpath('//a[@href]')

    for link in links:
        href = link.attrib['href']

        if fnmatch.fnmatch(href, glob_patt):

            if not href.startswith(('http://', 'https://' 'ftp://')):

                if href.startswith('/'):
                    href = host + href
                else:
                    parent_url = url.rsplit('/', 1)[0]
                    href = urlparse.urljoin(parent_url, href)

                    if urltools:
                        href = urltools.normalize(href)

            print href

用法如下:

getlinks.py http://stackoverflow.com/a/37758066/191246
getlinks.py http://stackoverflow.com/a/37758066/191246 "*users*"
getlinks.py http://fakedomain.mu/somepage.html "*.mp3"
另一答案
import urllib2
from bs4 import BeautifulSoup
a=urllib2.urlopen('http://dir.yahoo.com')
code=a.read()
soup=BeautifulSoup(code)
links=soup.findAll("a")
#To get href part alone
print links[0].attrs['href']
另一答案

为了完整起见,BeautifulSoup 4版本也使用了服务器提供的编码:

from bs4 import BeautifulSoup
import urllib2

resp = urllib2.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, from_encoding=resp.info().getparam('charset'))

for link in soup.find_all('a', href=True):
    print link['href']

或Python 3版本:

from bs4 import BeautifulSoup
import urllib.request

resp = urllib.request.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, from_encoding=resp.info().get_param('charset'))

for link in soup.find_all('a', href=True):
    print(link['href'])

以及使用requests library的版本,其编写将适用于Python 2和3:

from bs4 import BeautifulSoup
from bs4.dammit import EncodingDetector
import requests

resp = requests.get("http://www.gpsbasecamp.com/national-parks")
http_encoding = resp.encoding if 'charset' in resp.headers.get('content-type', '').lower() else None
html_encoding = EncodingDetector.find_declared_encoding(resp.content, is_html=True)
encoding = html_encoding or http_encoding
soup = BeautifulSoup(resp.content, from_encoding=encoding)

for link in soup.find_all('a', href=True):
    print(link['href'])

soup.find_all('a', href=True)调用找到所有具有<a>属性的href元素;跳过没有该属性的元素。

BeautifulSoup 3于2012年3月停止开发;新项目真的应该使用BeautifulSoup 4。

请注意,您应该将HTML从字节解码到BeautifulSoup。你可以告诉BeautifulSoup在HTTP响应头中找到的字符集以帮助解码,但这可能是错误的并且与HTML本身中的<meta>头信息冲突,这就是为什么上面使用BeautifulSoup内部类方法EncodingDetector.find_declared_encoding()来制作确保这样的嵌入式编码提示能够胜过错误配置的服务器。

使用requests时,如果响应具有response.encoding mimetype,则text/*属性默认为Latin-1,即使没有返回字符集。这与HTTP RFC一致但在与HTML解析一起使用时很痛苦,因此在Content-Type标头中未设置charset时应忽略该属性。

另一答案

其他人推荐使用BeautifulSoup,但使用lxml要好得多。尽管它的名字,它也用于解析和抓取HTML。它比BeautifulSo

以上是关于使用python和BeautifulSoup从网页检索链接的主要内容,如果未能解决你的问题,请参考以下文章

使用python和beautifulsoup4抓取网页后重复数据

使用 BeautifulSoup 查找网页上的特定文本

BeautifulSoup 不会从网页中提取所有表单

用于网页抓取的 Selenium 与 BeautifulSoup

使用 Python 和 BeautifulSoup(将网页源代码保存到本地文件中)

python爬虫从入门到放弃之 BeautifulSoup库的使用