Câu trả lời:
Đây là một đoạn ngắn sử dụng lớp SoupStrainer trong 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'])
Tài liệu BeautifulSoup thực sự khá tốt và bao gồm một số tình huống điển hình:
https://www.crummy.com/software/BeautitableSoup/bs4/doc/
Chỉnh sửa: Lưu ý rằng tôi đã sử dụng lớp SoupStrainer vì nó hiệu quả hơn một chút (bộ nhớ và tốc độ khôn ngoan), nếu bạn biết trước những gì bạn đang phân tích cú pháp.
/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
. Thay vào đó tôi thấy có một cái gì đó được gọi has_key
và nó hoạt động.
Để hoàn thiện, phiên bản BeautifulSoup 4, cũng sử dụng mã hóa được cung cấp bởi máy chủ:
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'])
hoặc phiên bản 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']
và một phiên bản sử dụng requests
thư viện , như được viết sẽ hoạt động trong cả Python 2 và 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'])
Cuộc soup.find_all('a', href=True)
gọi tìm thấy tất cả các <a>
yếu tố có href
thuộc tính; các phần tử không có thuộc tính được bỏ qua.
BeautifulSoup 3 đã ngừng phát triển vào tháng 3 năm 2012; dự án mới thực sự nên sử dụng BeautifulSoup 4, luôn luôn.
Lưu ý rằng bạn nên để giải mã HTML từ byte sang BeautifulSoup . Bạn có thể thông báo cho BeautifulSoup về bộ ký tự được tìm thấy trong các tiêu đề phản hồi HTTP để hỗ trợ giải mã, nhưng điều này có thể sai và mâu thuẫn với <meta>
thông tin tiêu đề được tìm thấy trong chính HTML, đó là lý do tại sao ở trên sử dụng phương thức lớp bên trong BeautifulSoup EncodingDetector.find_declared_encoding()
để đảm bảo rằng gợi ý mã hóa nhúng như vậy giành chiến thắng trên một máy chủ được cấu hình sai.
Với requests
, response.encoding
thuộc tính mặc định là Latin-1 nếu phản hồi có mô text/*
phỏng, ngay cả khi không có ký tự nào được trả về. Điều này phù hợp với các RFC HTTP nhưng đau đớn khi được sử dụng với phân tích cú pháp HTML, vì vậy bạn nên bỏ qua thuộc tính đó khi không charset
được đặt trong tiêu đề Kiểu nội dung.
SoupStrainer
ý bạn là gì? Nó không đi đâu cả, nó vẫn là một phần của dự án .
Những người khác đã khuyến nghị BeautifulSoup, nhưng sử dụng lxml sẽ tốt hơn nhiều . Mặc dù tên của nó, nó cũng là để phân tích cú pháp và cạo HTML. Nó nhanh hơn nhiều so với BeautifulSoup và thậm chí nó còn xử lý HTML "hỏng" tốt hơn BeautifulSoup (yêu cầu của họ để nổi tiếng). Nó cũng có API tương thích cho BeautifulSoup nếu bạn không muốn tìm hiểu API lxml.
Không còn lý do để sử dụng BeautifulSoup nữa, trừ khi bạn sử dụng Google App Engine hoặc một cái gì đó mà mọi thứ không hoàn toàn là Python không được phép.
lxml.html cũng hỗ trợ các bộ chọn CSS3 vì vậy loại điều này là tầm thường.
Một ví dụ với lxml và xpath sẽ như thế này:
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
làm trình phân tích cú pháp mặc định nếu được cài đặt.
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'
Đoạn mã sau là để lấy tất cả các liên kết có sẵn trong một trang web bằng cách sử dụng urllib2
và 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'))
Dưới mui xe BeautifulSoup hiện sử dụng lxml. Yêu cầu, lxml & danh sách hiểu biết làm cho một kết hợp giết người.
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]
Trong danh sách comp, "if '//' và 'url.com' không phải trong x" là một phương pháp đơn giản để xóa danh sách url của các url điều hướng 'nội bộ' của trang web, v.v.
chỉ để nhận các liên kết, không có B.soup và 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]
Đối với các hoạt động phức tạp hơn, tất nhiên BSoup vẫn được ưu tiên.
<a
và href
? Nói rel="nofollow"
hay onclick="..."
thậm chí chỉ là một dòng mới? stackoverflow.com/questions/1732348/ từ
Kịch bản này thực hiện những gì bạn đang tìm kiếm, nhưng cũng giải quyết các liên kết tương đối đến các liên kết tuyệt đối.
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
Để tìm tất cả các liên kết, trong ví dụ này, chúng tôi sẽ sử dụng mô-đun urllib2 cùng với re.module * Một trong những hàm mạnh nhất trong mô-đun re là "re.findall ()". Trong khi re.search () được sử dụng để tìm kết quả khớp đầu tiên cho mẫu, re.findall () tìm tất cả các kết quả khớp và trả về chúng dưới dạng danh sách các chuỗi, với mỗi chuỗi đại diện cho một kết quả khớp *
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
Tại sao không sử dụng biểu thức thông thường:
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)
phương tiện ở đâu một cách hiệu quả ? cảm ơn!
Liên kết có thể nằm trong nhiều thuộc tính khác nhau để bạn có thể chuyển danh sách các thuộc tính đó để chọn
ví dụ: với thuộc tính src và href (ở đây tôi đang sử dụng toán tử start with ^ để chỉ định rằng một trong hai giá trị thuộc tính này bắt đầu bằng http. Bạn có thể điều chỉnh điều này theo yêu cầu
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 ^ = giá trị]
Đại diện cho các phần tử có tên thuộc tính của attr có giá trị được tiền tố (đứng trước) theo giá trị.
Dưới đây là một ví dụ sử dụng @ars câu trả lời được chấp nhận và các BeautifulSoup4
, requests
và wget
mô-đun để xử lý các tải.
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)
Tôi đã tìm thấy câu trả lời bởi @ Blairg23 hoạt động, sau khi sửa lỗi sau (bao gồm kịch bản không hoạt động chính xác):
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)
Đối với Python 3:
urllib.parse.urljoin
phải được sử dụng để có được URL đầy đủ thay thế.
Trình phân tích cú pháp riêng của BeatiouslySoup có thể bị chậm. Có thể khả thi hơn khi sử dụng lxml có khả năng phân tích cú pháp trực tiếp từ một URL (với một số hạn chế được đề cập dưới đây).
import lxml.html
doc = lxml.html.parse(url)
links = doc.xpath('//a[@href]')
for link in links:
print link.attrib['href']
Mã ở trên sẽ trả về các liên kết như hiện tại và trong hầu hết các trường hợp chúng sẽ là các liên kết tương đối hoặc tuyệt đối từ gốc trang. Vì trường hợp sử dụng của tôi là chỉ trích xuất một loại liên kết nhất định, bên dưới là phiên bản chuyển đổi các liên kết thành URL đầy đủ và tùy chọn chấp nhận mô hình toàn cầu như thế nào *.mp3
. Mặc dù vậy, nó sẽ không xử lý các dấu chấm đơn và kép trong các đường dẫn tương đối, nhưng cho đến nay tôi không có nhu cầu về nó. Nếu bạn cần phân tích các đoạn URL có chứa ../
hoặc ./
sau đó urlparse.urljoin có thể có ích.
LƯU Ý : Phân tích cú pháp url lxml trực tiếp không xử lý tải từ https
và không chuyển hướng, vì vậy, vì lý do này, phiên bản bên dưới đang sử dụng 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
Cách sử dụng như sau:
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
Chỉ có thể xử lý đầu vào hợp lệ, làm thế nào nó có thể thay thế BeautifulSoup
?
lxml.html
là khoan dung hơn một chút so với lxml.etree
. Nếu đầu vào của bạn không được định dạng tốt thì bạn có thể đặt rõ ràng trình phân tích cú pháp BeautifulSoup: lxml.de/elementsoup.html . Và nếu bạn đồng hành cùng BeatiouslySoup thì BS3 là lựa chọn tốt hơn.
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']
Có thể có nhiều liên kết trùng lặp cùng với cả liên kết bên ngoài và bên trong. Để phân biệt giữa hai và chỉ nhận các liên kết duy nhất bằng cách sử dụng các bộ:
# 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)