Подтвердить что ты не робот

Извлекать ссылки с веб-страницы с помощью python и BeautifulSoup

Как я могу получить ссылки на веб-страницу и скопировать URL-адрес ссылок с помощью Python?

4b9b3361

Ответ 1

Вот небольшой фрагмент с использованием класса SoupStrainer в BeautifulSoup:

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, потому что он немного более эффективен (с точки зрения памяти и скорости), если вы знаете, что вы анализируете заранее.

Ответ 2

Для полноты использования версия 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) находит все <a> элементы, которые имеют атрибут href; элементы без атрибута пропускаются.

BeautifulSoup 3 прекратил разработку в марте 2012 года; новые проекты действительно должны использовать BeautifulSoup 4, всегда.

Обратите внимание, что вы должны оставить декодирование HTML из байтов в BeautifulSoup. Вы можете сообщить BeautifulSoup из набора символов, найденного в заголовках ответа HTTP, чтобы помочь в декодировании, но это может быть неправильным и противоречивым с информацией заголовка <meta>, найденной в самом HTML, поэтому в приведенном выше примере используется метод внутреннего класса BeautifulSoup EncodingDetector.find_declared_encoding(), чтобы убедиться, что такие встроенные подсказки для кодирования выиграли от неправильно сконфигурированного сервера.

С requests атрибут response.encoding по умолчанию имеет значение Latin-1, если ответ имеет тип mimetype text/*, даже если не было возвращено никаких символов. Это согласуется с HTTP RFC, но больно при использовании с разбором HTML, поэтому вы должны игнорировать этот атрибут, если в заголовке Content-Type не установлен charset.

Ответ 3

Другие рекомендовали BeautifulSoup, но гораздо лучше использовать lxml. Несмотря на свое название, он также предназначен для разбора и очистки HTML. Это намного, намного быстрее, чем BeautifulSoup, и даже обрабатывает "сломанный" HTML лучше, чем BeautifulSoup (их претензии на славу). Он также имеет API совместимости для BeautifulSoup, если вы не хотите изучать API-интерфейс lxml.

Ian Blicking согласен.

Нет смысла использовать BeautifulSoup больше, если вы не используете Google App Engine или что-то там, где не разрешено ничего, кроме Python.

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

Ответ 4

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'

Ответ 5

Следующий код предназначен для извлечения всех ссылок, доступных на веб-странице, используя urllib2 и BeautifulSoup4:

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'))

Ответ 6

Под капотом 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]

В списке comp "if" // и "url.com" не в x "- это простой способ сгладить список URL-адресов внутренних" навигационных URL-адресов "сайтов и т.д.

Ответ 7

Чтобы найти все ссылки, в этом примере мы будем использовать модуль urllib2 вместе с re.module * Одна из самых мощных функций в модуле re - "re.findall()". Хотя re.search() используется для поиска первого совпадения для шаблона, re.findall() находит все совпадений и возвращает их как список строк, причем каждая строка представляет одно совпадение *

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

Ответ 8

только для получения ссылок, без B.soup и regex:

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 по-прежнему предпочтительнее.

Ответ 9

Почему бы не использовать регулярные выражения:

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]))

Ответ 10

Этот script выполняет то, что вы ищете, но также разрешает относительные ссылки на абсолютные ссылки.

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

Ответ 11

Ссылки могут быть в пределах множества атрибутов, чтобы вы могли передать список этих атрибутов для выбора

например, с атрибутом 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, значению которого предшествует (предшествует) значение.

Ответ 12

Собственный парсер 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-адреса и которая при необходимости принимает шаблон glob, например *.mp3. Однако он не будет обрабатывать одиночные и двойные точки в относительных путях, но до сих пор у меня не было необходимости в этом. Если вам нужно проанализировать фрагменты URL-адресов, содержащие ../ или ./, то urlparse.urljoin может пригодиться.

ПРИМЕЧАНИЕ. Прямой синтаксический анализ URL-адресов lxml не обрабатывает загрузку с 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"

Ответ 13

Здесь приведен пример использования принятых ответов @ars и модулей BeautifulSoup4, requests и 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)

Ответ 14

Я нашел ответ от @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-адреса.

Ответ 15

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']

Ответ 16

Может быть много повторяющихся ссылок вместе с внешними и внутренними ссылками. Чтобы различать их и получать уникальные ссылки с помощью наборов:

# Python 3.
import urllib    
from bs4 import BeautifulSoup

url = "http://www.espncricinfo.com/"
resp = urllib.request.urlopen(url)
# Get server encoding per recommendation of Martijn Pieters.
soup = BeautifulSoup(resp, from_encoding=resp.info().get_param('charset'))  
external_links = set()
internal_links = set()
for line in soup.find_all('a'):
    link = line.get('href')
    if not link:
        continue
    if link.startswith('http'):
        external_links.add(link)
    else:
        internal_links.add(link)

# Depending on usage, full internal links may be preferred.
full_internal_links = {
    urllib.parse.urljoin(url, internal_link) 
    for internal_link in internal_links
}

# Print all unique external and full internal links.
for link in external_links.union(full_internal_links):
    print(link)