Відповіді:
Ось короткий фрагмент із використанням класу SoupStrainer в BeautifulSoup:
import httplib2
from bs4 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 насправді є досить хорошою і охоплює ряд типових сценаріїв:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
Редагувати: Зауважте, що я використовував клас SoupStrainer, оскільки це трохи ефективніше (пам'ять та швидкість), якщо ви знаєте, що ви розбираєте заздалегідь.
/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."
has_attr
. Натомість я бачу, що щось називається, has_key
і це працює.
Для повноти, версія BeautifulSoup 4, використовуючи також кодування, надане сервером:
from bs4 import BeautifulSoup
import urllib.request
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib.request.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().get_param('charset'))
for link in soup.find_all('a', href=True):
print(link['href'])
або версія Python 2:
from bs4 import BeautifulSoup
import urllib2
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
resp = urllib2.urlopen("http://www.gpsbasecamp.com/national-parks")
soup = BeautifulSoup(resp, parser, from_encoding=resp.info().getparam('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
parser = 'html.parser' # or 'lxml' (preferred) or 'html5lib', if installed
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, parser, 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, якщо відповідь має text/*
міметик, навіть якщо жоден набір символів не повертається. Це узгоджується з HTTP RFC, але болісно при використанні з розбором HTML, тому слід ігнорувати цей атрибут, коли charset
в заголовку Content-Type не встановлено жодне .
SoupStrainer
ти маєш на увазі? Він нікуди не дійшов, він все ще є частиною проекту .
Інші рекомендують BeautifulSoup, але набагато краще використовувати lxml . Незважаючи на свою назву, він також призначений для розбору та скребки HTML. Це набагато, набагато швидше, ніж BeautifulSoup, і він навіть обробляє "зламаний" HTML краще, ніж BeautifulSoup (їх претензія на славу). Він також має API сумісності для BeautifulSoup, якщо ви не хочете вивчати API lxml.
Немає жодної причини використовувати 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
lxml
як аналізатор за замовчуванням, якщо встановлений.
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'
Наступний код - це отримати всі посилання, доступні на веб-сторінці, використовуючи 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'))
Під кришкою 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 "not in x" - це простий метод для скрупування списку URL-адрес "внутрішніх" навігаційних сайтів тощо.
тільки для отримання посилань, без 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 все ще віддається перевагою.
<a
і href
? Скажіть, rel="nofollow"
або onclick="..."
навіть просто новий рядок? stackoverflow.com/questions/1732348 / ...
Цей сценарій робить те, що ви шукаєте, але також вирішує відносні посилання на абсолютні посилання.
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
Щоб знайти всі посилання, ми в цьому прикладі використовуватимемо модуль 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
Чому б не використовувати регулярні вирази:
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)
означає? Дякую!
Посилання можуть містити різні атрибути, щоб ви могли передати список цих атрибутів для вибору
наприклад, з атрибутом 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 ^ = значення]
Представляє елементи з назвою атрибута attr, значення якого є префіксом (попереднім) за значенням.
Ось приклад використання @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)
Я знайшов відповідь від @ 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 може бути повільним. Можливо, більш доцільним буде використання 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, може стати в нагоді.
ПРИМІТКА : Прямий розбір 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"
lxml
може обробляти тільки дійсний ввід, як його можна замінити BeautifulSoup
?
lxml.html
, що трохи поблажливіше ніж lxml.etree
. Якщо ваш вклад не сформований, ви можете явно встановити парсер BeautifulSoup: lxml.de/elementsoup.html . І якщо ви все-таки йдете з BeatifulSoup, тоді BS3 - кращий вибір.
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']
Тут може бути багато повторюваних посилань, як зовнішніх, так і внутрішніх. Щоб розмежувати обидва та просто отримати унікальні посилання за допомогою наборів:
# 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)