web-dev-qa-db-ja.com

pythonおよびBeautifulSoupを使用してWebページからリンクを取得します

Pythonを使用してWebページのリンクを取得し、リンクのURLアドレスをコピーするにはどうすればよいですか?

126
NepUS

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クラスを使用したことに注意してください。事前に解析していることがわかっている場合は、SoupStrainerクラスの方が少し効率的です(メモリと速度の面で)。

173
ars

完全を期すために、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ライブラリー を使用するバージョン。これは、書かれているように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)呼び出しは、href属性を持つすべての<a>要素を見つけます。属性のない要素はスキップされます。

BeautifulSoup 3は2012年3月に開発を停止しました。新しいプロジェクトでは、常にBeautifulSoup 4を使用する必要があります。

HTMLのデコードをバイトからBeautifulSoupに任せることに注意してください。 BeautifulSoupにHTTP応答ヘッダーにある文字セットを通知してデコードを支援できますが、これはが間違っていて<meta>と競合する可能性がありますHTML自体にあるヘッダー情報。上記がBeautifulSoup内部クラスメソッドEncodingDetector.find_declared_encoding()を使用して、そのような埋め込みエンコーディングヒントが誤って構成されたサーバーに勝つことを確認する理由です。

requestsを使用すると、応答にresponse.encoding mimetypeがある場合、文字セットが返されなかった場合でも、text/*属性はデフォルトでLatin-1になります。これはHTTP RFCと一致しますが、HTML解析で使用すると苦痛を伴うため、Content-Typeヘッダーにcharsetが設定されていない場合はその属性を無視する必要があります。

61
Martijn Pieters

BeautifulSoupを推奨している人もいますが、 lxml を使用する方がはるかに優れています。名前にもかかわらず、HTMLの解析とスクレイピングにも使用されます。 BeautifulSoupよりもはるかに高速であり、BeautifulSoup(名声を主張)よりも「壊れた」HTMLをうまく処理します。 lxml APIを学習したくない場合は、BeautifulSoupの互換性APIもあります。

イアン・ブリックが同意する

Google App Engineまたは純粋にPythonが許可されていないものを使用している場合を除き、BeautifulSoupを使用する理由はもうありません。

lxml.htmlはCSS3セレクタもサポートしているため、この種のことは簡単です。

lxmlとxpathを使用した例は次のようになります:

import urllib
import lxml.html
connection = urllib.urlopen('http://www.nytimes.com')

dom =  lxml.html.fromstring(connection.read())

for link in dom.xpath('//a/@href'): # select the url in href for all a tags(links)
    print link
48
aehlke
import urllib2
import BeautifulSoup

request = urllib2.Request("http://www.gpsbasecamp.com/national-parks")
response = urllib2.urlopen(request)
soup = BeautifulSoup.BeautifulSoup(response)
for a in soup.findAll('a'):
  if 'national-park' in a['href']:
    print 'found a url with national-park in the link'
27
Andrew Johnson

次のコードは、urllib2およびBeautifulSoup4を使用して、Webページで使用可能なすべてのリンクを取得します。

import urllib2
from bs4 import BeautifulSoup

url = urllib2.urlopen("http://www.espncricinfo.com/").read()
soup = BeautifulSoup(url)

for line in soup.find_all('a'):
    print(line.get('href'))
10
Sentient07

内部的にBeautifulSoupはlxmlを使用します。リクエスト、lxml、リストの内包表記は、素晴らしい組み合わせです。

import requests
import lxml.html

dom = lxml.html.fromstring(requests.get('http://www.nytimes.com').content)

[x for x in dom.xpath('//a/@href') if '//' in x and 'nytimes.com' not in x]

リストコンプで、「if '//' and 'url.com' not not x」は、サイトのURLリスト「内部」ナビゲーションURLなどをスクラブする簡単な方法です。

8
cheekybastard

すべてのリンクを見つけるには、この例ではurllib2モジュールとre.module * reモジュールの最も強力な関数は「re.findall()」です。 re.search()を使用してパターンの最初の一致を検索しますが、re.findall()は一致を検索allし、それらをリストとして返します各文字列が1つの一致を表す文字列*

import urllib2

import re
#connect to a URL
website = urllib2.urlopen(url)

#read html code
html = website.read()

#use re.findall to get all the links
links = re.findall('"((http|ftp)s?://.*?)"', html)

print links
4
Mayur Ingle

b.soupと正規表現なしでリンクを取得するためだけに:

import urllib2
url="http://www.somewhere.com"
page=urllib2.urlopen(url)
data=page.read().split("</a>")
tag="<a href=\""
endtag="\">"
for item in data:
    if "<a href" in item:
        try:
            ind = item.index(tag)
            item=item[ind+len(tag):]
            end=item.index(endtag)
        except: pass
        else:
            print item[:end]

より複雑な操作の場合は、もちろんBSoupが優先されます。

3
ghostdog74

このスクリプトはあなたの探していることをしますが、相対リンクを絶対リンクに解決します。

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
3
Ricky Wilson

正規表現を使用しない理由:

import urllib2
import re
url = "http://www.somewhere.com"
page = urllib2.urlopen(url)
page = page.read()
links = re.findall(r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page)
for link in links:
    print('href: %s, HTML text: %s' % (link[0], link[1]))
3
ahmadh

リンクはさまざまな属性内にあるため、それらの属性のリストを渡して選択することができます

たとえば、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)

属性=値セレクター

[attr ^ = value]

値の前に値が付いている(前にある)属性の属性名がattrの要素を表します。

2
QHarr

ダウンロードを処理するために@arsの回答とBeautifulSoup4requests、およびwgetモジュールを使用した例を次に示します。

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)
1
Blairg23

BeatifulSoup自身のパーサーは遅くなる可能性があります。 lxmlを使用する方が適切かもしれません(URLから直接解析できます)(以下で説明するいくつかの制限があります)。

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フラグメントを解析する必要がある場合は、 rlparse.urljoin が便利です。

NOTE:直接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 __== '__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"
1
ccpizza

次の修正の後、@ 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の場合:

代わりに完全なURLを取得するには、urllib.parse.urljoinを使用する必要があります。

1
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']
0
Tilak Patidar