Вопрос по python – получить ссылки с веб-страницы, используя python и BeautifulSoup

115

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

Ваш Ответ

14   ответов
3

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

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
167

lSoup:

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.htm

Редактировать: обратите внимание, что я использовал класс SoupStrainer, потому что он немного более эффективен (с точки зрения памяти и скорости), если вы знаете, что вы анализируете заранее.

+ 1, использовать суповое ситечко - отличная идея, поскольку позволяет обойтись без ненужного анализа, когда все, что вам нужно, это ссылки. Evan Fosmark
Берегись:/usr/local/lib/python2.7/site-packages/bs4/__init__.py:128: UserWarning: The "parseOnlyThese" argument to the BeautifulSoup constructor has been renamed to "parse_only." BenDundee
На версии 3.2.1 BeautifulSoup нетhas_attr. Вместо этого я вижу что-то под названиемhas_key и это работает. user2796118
Обновление для python3 john doe
от bs4 import BeautifulSoup. (не из BeautifulSoup импортировать BeautifulSoup ..) требуется коррекция. Rishabh Agrahari
56

версия 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'])

Thesoup.find_all('a', href=True) вызов находит все<a> элементы, которые имеютhref атрибут; элементы без атрибута пропускаются.

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

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

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

Есть ли что-то вроде StrainedSoup для bs4? (Мне это не нужно сейчас, но просто интересно, если бы вы могли добавить это) Antti Haapala
@ AnttiHaapala:SoupStrainer ты имеешь в виду? Это никуда не делась, это все еще часть проекта. Martijn Pieters♦
48

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

Ян Бликинг соглашается.

Больше нет смысла использовать 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
BeautifulSoup 4 будет использоватьlxml как парсер по умолчанию, если установлен. Martijn Pieters♦
26
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'
Это решило проблему с моим кодом. Спасибо R J
8

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 '//» и «url.com» не в x »- это простой метод очистки списка URL-адресов« внутренних »URL-адресов сайтов и т. Д.

Если это репост, почему оригинальное сообщение не содержит: 1. запросы 2. список комп 3. логика для очистки внутренних и нежелательных ссылок сайта ?? Попробуйте сравнить результаты двух постов, мой список отлично справляется с поиском ненужных ссылок. cheekybastard
ОП не запрашивал эти функции, и часть, которую он запрашивал, уже была опубликована и решена с использованием того же метода, что и вы. Тем не менее, я уберу понижение рейтинга, так как понимание списка действительно приносит пользу людям, которым нужны эти функции, и вы явно упоминаете их в тексте поста. Также вы можете использовать rep:) dotancohen
8

доступных на веб-странице, с использованием 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'))
5

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


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

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 все еще предпочтителен.

А если, например, есть что-то среднее между<a а такжеhref? Сказатьrel="nofollow" илиonclick="..." или просто новая строка? / Stackoverflow.com вопросы / 1732348 / ... dimo414
есть ли способ отфильтровать только некоторые ссылки с этим? как, скажем, я хочу только ссылки, которые имеют "Эпизод" в ссылке? nwgat
3

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

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]))
Мне бы хотелось понять это, где я могу эффективно узнать, что(r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page) средства? Благодарность user1063287
Действительно плохая идея. Поврежденный HTML везде. Ufoguy
Почему бы не использовать регулярные выражения для анализа html: / Stackoverflow.com вопросы / 1732348 / ... allcaps
@ user1063287, в Интернете полно учебников по регулярным выражениям. Это стоит того, чтобы почитать пару. Хотя RE могут быть действительно запутанными, тот, о котором вы спрашиваете, довольно прост. alexis
1

Вот пример использования @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)
1

после следующего исправления (охватывающего сценарий, когда он не работал правильно):

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.

1

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

НОТ: Прямой синтаксический анализ 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"
lxml может обрабатывать только действительные данные, как он может заменитьBeautifulSoup? alexis
@ alexis: я думаюlxml.html немного мягче, чемlxml.etree. Если ваш ввод не правильно сформирован, вы можете явно установить синтаксический анализатор BeautifulSoup: Lxml.de / elementsoup.html. И если вы используете BeatifulSoup, тогда BS3 - лучший выбор. ccpizza
0
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']

Похожие вопросы