2009-07-03 53 views

Trả lời

139

Dưới đây là một đoạn ngắn bằng cách sử dụng lớp SoupStrainer trong BeautifulSoup:

import httplib2 
from BeautifulSoup import BeautifulSoup, SoupStrainer 

http = httplib2.Http() 
status, response = http.request('http://www.nytimes.com') 

for link in BeautifulSoup(response, parseOnlyThese=SoupStrainer('a')): 
    if link.has_attr('href'): 
     print link['href'] 

Các tài liệu BeautifulSoup thực sự là khá tốt, và bao gồm một số tình huống điển hình:

http://www.crummy.com/software/BeautifulSoup/documentation.html

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 những gì bạn đang phân tích cú pháp trước.

+10

+1, sử dụng bộ lọc súp là một ý tưởng tuyệt vời vì nó cho phép bạn phá vỡ rất nhiều phân tích cú pháp không cần thiết khi tất cả những gì bạn theo sau là các liên kết. –

+0

Tôi đã chỉnh sửa để thêm một lời giải thích tương tự trước khi tôi nhìn thấy lời bình luận của Evan. Cảm ơn bạn đã lưu ý rằng, mặc dù! – ars

+0

cảm ơn, điều này giải quyết vấn đề của tôi, với điều này tôi hoàn thành proyect của tôi cảm ơn rất nhiều – NepUS

25
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' 
4

chỉ để nhận được các liên kết, mà không 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] 

cho các hoạt động phức tạp hơn, tất nhiên BSoup vẫn ưa thích.

+5

Và nếu, ví dụ, có một cái gì đó inbetween ' dimo414

+0

có cách nào để lọc ra chỉ một số liên kết với điều này? như tôi nói chỉ muốn liên kết có "Tập" trong liên kết? – wiak

44

Những người khác đã đề xuất BeautifulSoup, nhưng tốt hơn hết là sử dụng lxml. 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 rất nhiều so với BeautifulSoup và thậm chí còn xử lý HTML "bị hỏng" tốt hơn so với BeautifulSoup (yêu cầu của họ đối với danh 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.

Ian Blicking agrees.

Không có lý do gì để sử dụng BeautifulSoup nữa, trừ khi bạn đang sử dụng Google App Engine hoặc thứ gì đó không hoàn toàn là Python không được phép.

lxml.html cũng hỗ trợ bộ chọn CSS3 để loại điều này là tầm thường.

Một ví dụ với lxml và xpath sẽ trông 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 
+16

BeautifulSoup 4 sẽ sử dụng 'lxml' làm trình phân tích cú pháp mặc định nếu được cài đặt. –

2

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

Tôi rất muốn có thể hiểu được điều này, ở đâu tôi có thể tìm ra những gì '(r ' (. *?) ", trang)" nghĩa là gì? cảm ơn! – user1063287

+7

Thực sự là một ý tưởng tồi. HTML bị hỏng ở khắp mọi nơi. – Ufoguy

+2

Tại sao không sử dụng cụm từ thông dụng để phân tích cú pháp html: http: // stackoverflow.com/questions/1732348/regex-match-open-tags-ngoại trừ-xhtml-self-contained-tags? page = 1 & tab = votes # 1732454 – allcaps

6

Dưới mui xe BeautifulSoup bây giờ sử dụng lxml. Yêu cầu, lxml & việc hiểu danh sách làm cho một combo sát thủ.

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' not in x" là một phương pháp đơn giản để xóa danh sách url của url điều hướng nội bộ của trang web, v.v.

+1

Nếu nó là một repost, tại sao không phải là bài gốc bao gồm: 1. yêu cầu 2.list comp 3. logic để chà trang nội bộ & liên kết rác ?? Hãy thử và so sánh kết quả của hai bài viết, danh sách comp của tôi thực hiện một công việc tốt đáng ngạc nhiên khi quét các liên kết rác. – cheekybastard

+0

OP không yêu cầu các tính năng đó và phần mà anh ta yêu cầu đã được đăng và giải quyết bằng cách sử dụng cùng một phương thức giống như bạn đăng. Tuy nhiên, tôi sẽ loại bỏ các downvote như danh sách hiểu không thêm giá trị cho những người mà muốn những tính năng và bạn làm rõ ràng đề cập đến họ trong cơ thể của bài viết. Ngoài ra, bạn có thể sử dụng đại diện :) – dotancohen

8

Các mã sau đây là để lấy tất cả các liên kết có sẵn trong một trang web 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')) 
39

Đối với đầy đủ sake, phiên bản BeautifulSoup 4, làm cho việc sử dụng mã hóa được cung cấp bởi các máy chủ cũng như:

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

hoặc phiên bản 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']) 

và một phiên bản sử dụng requests library, mà như viết sẽ làm việc trong cả hai Python 2 và 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']) 

Cuộc gọi soup.find_all('a', href=True) tìm tất cả <a> yếu tố mà có một thuộc tính href; 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; các 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 thành BeautifulSoup. Bạn có thể thông báo cho BeautifulSoup về các ký tự trả lời được tìm thấy trong tiêu đề phản hồi HTTP để hỗ trợ giải mã, nhưng có thể sai và xung đột với thông tin tiêu đề <meta> được tìm thấy trong chính HTML, đó là lý do tại sao sử dụng phương thức lớp nội bộ BeautifulSoup EncodingDetector.find_declared_encoding() để đảm bảo rằng các gợi ý mã hóa được nhúng như vậy sẽ giành chiến thắng trên máy chủ được định cấu hình sai.

Với requests, thuộc tính response.encoding mặc định là Latin-1 nếu đáp ứng có giá trị text/* mimetype, ngay cả khi không có ký tự nào được trả về. Điều này phù hợp với 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 có charset được đặt trong tiêu đề Kiểu nội dung.

+0

Có điều gì đó giống như StrainedSoup cho bs4 không? (Tôi không cần nó bây giờ nhưng chỉ tự hỏi, nếu có bạn có thể muốn thêm rằng) –

+0

@AnttiHaapala: 'SoupStrainer' bạn có nghĩa là gì? Nó [không đi đâu cả, nó vẫn là một phần của dự án] (https://www.crummy.com/software/BeautifulSoup/bs4/doc/#soupstrainer). –

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

Tập lệnh này thực hiện những gì bạn đang tìm kiếm, Nhưng cũng giải quyết liên kết tương đối đến 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 
5

Để tìm tất cả các liên kết, trong ví dụ này, chúng ta sẽ sử dụng các mô-đun urllib2 cùng với re.module * Một trong những chức năng mạnh mẽ nhất trong các mô-đun lại là "re.findall() ". Trong khi re.search() được sử dụng để tìm các trận đấu đầu tiên cho một mẫu, re.findall() tìm thấy tất cả các trận đấu và trả họ như là một danh sách các chuỗi, mỗi chuỗi đại diện cho một trận đấu *

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 
1

BeatifulBộ phân tích cú pháp của riêng mình có thể bị chậm. Có thể khả thi hơn để sử dụng lxml có khả năng phân tích trực tiếp từ URL (với một số hạn chế được đề cập bên dưới).

import lxml.html 

doc = lxml.html.parse(url) 

links = doc.xpath('//a[@href]') 

for link in links: 
    print link.attrib['href'] 

Đoạn mã trên sẽ trả lại các liên kết như là, và trong nhiều trường hợp họ sẽ liên kết tương đối hoặc tuyệt đối từ gốc trang web. Vì trường hợp sử dụng của tôi 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 liên kết thành URL đầy đủ và tùy chọn chấp nhận mẫu hình cầu như *.mp3. Nó sẽ không xử lý dấu chấm đơn và đôi trong các đường dẫn tương đối mặc dù, nhưng cho đến nay tôi không có nhu cầu cho nó. Nếu bạn cần phân tích cú pháp các đoạn URL chứa ../ hoặc ./ thì urlparse.urljoin có thể hữu ích.

LƯU Ý: lxml Direct url phân tích cú pháp không xử lý tải từ https và không làm chuyển hướng, nên vì lý do này các phiên bản dưới đây được 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 

Việc 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" 
+0

'lxml' chỉ có thể xử lý thông tin nhập hợp lệ, làm thế nào nó có thể thay thế' BeautifulSoup'? – alexis

+0

@alexis: Tôi nghĩ rằng 'lxml.html' hơi khoan dung hơn' lxml.etree'. Nếu đầu vào của bạn không được định dạng đúng thì bạn có thể đặt trình phân tích cú pháp BeautifulSoup một cách rõ ràng: http://lxml.de/elementsoup.html. Và nếu bạn làm với BeatifulSoup thì BS3 là một lựa chọn tốt hơn. – ccpizza

0

Dưới đây là một ví dụ sử dụng @ars chấp nhận câu trả lời và BeautifulSoup4, requests, và wget module để 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) 
0

Tôi tìm thấy câu trả lời của @ Blairg23 làm việc, sau đợt điều chỉnh sau (bao gồm các kịch bản mà 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ế.

Các vấn đề liên quan