अजगर और ब्यूटीफुल का उपयोग करके वेब पेज से लिंक प्राप्त करें


141

मैं एक वेबपेज के लिंक कैसे प्राप्त कर सकता हूं और पायथन का उपयोग करके लिंक के यूआरएल पते की नकल कर सकता हूं?

जवाबों:


193

यहां सुंदरसुपर में सूपस्ट्रेनर क्लास का उपयोग करते हुए एक छोटा सा स्निपेट दिया गया है:

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

सुंदरसुपर प्रलेखन वास्तव में काफी अच्छा है, और कई विशिष्ट परिदृश्यों को शामिल किया गया है:

https://www.crummy.com/software/BeautifulSoup/bs4/doc/

संपादित करें: ध्यान दें कि मैंने सूपस्ट्रेनर क्लास का उपयोग किया था क्योंकि यह थोड़ा अधिक कुशल (मेमोरी और स्पीड वार) है, अगर आपको पता है कि आप पहले से क्या पार्स कर रहे हैं।


13
1, सूप स्ट्रेनर का उपयोग करना एक महान विचार है क्योंकि यह आपको बहुत सारे अनावश्यक पार्सिंग को दरकिनार करने की अनुमति देता है जब आप सभी लिंक होते हैं।
इवान फॉसमार्क

4
सिर ऊपर:/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

27
BeautifulSoup के संस्करण 3.2.1 में कोई नहीं है has_attr। इसके बजाय मैं देखता हूं कि कुछ कहा जाता है has_keyऔर यह काम करता है।

2
पाइथन 3 के लिए अपडेट
जॉन डो

7
bs4 आयात से BeautifulSoup। (नहीं सुंदर से आयात। सुंदर आयात ..) सुधार की जरूरत है।
ऋषभ अग्रहरी ११'१ Agra को Agra

67

संपूर्णता के लिए, 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'])

या अजगर 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पुस्तकालय का उपयोग करने वाला एक संस्करण , जो लिखा गया है , पायथन 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विशेषता; विशेषता के बिना तत्वों को छोड़ दिया जाता है।

सुंदरसुपर 3 ने मार्च 2012 में विकास रोक दिया; नई परियोजनाओं को हमेशा सुंदर 4 एस का उपयोग करना चाहिए, हमेशा।

ध्यान दें कि आपको HTML को बाइट्स से सुंदरसुपर को डिकोड करना छोड़ना चाहिए । डिकोडिंग में सहायता करने के लिए आप HTTP रिस्पांस हेडर में पाए जाने वाले कैरेक्टरसेट के ब्यूटीफुलस को सूचित कर सकते हैं, लेकिन यह गलत हो सकता है और <meta>HTML में पाए जाने वाले हेडर की जानकारी के साथ परस्पर विरोधी हो सकता है, यही कारण है कि उपरोक्त सुंदरसेप आंतरिक वर्ग विधि EncodingDetector.find_declared_encoding()का उपयोग यह सुनिश्चित करने के लिए करता है कि ऐसे एम्बेडेड एन्कोडिंग संकेत एक गलत सर्वर पर जीतते हैं।

साथ requests, response.encodingलैटिन -1 के लिए विशेषता चूक प्रतिक्रिया एक है, तो text/*भले ही कोई characterset वापस आ गया था माइम प्रकार,। यह एचटीटीपी RFC के अनुरूप है, लेकिन HTML पार्सिंग के साथ प्रयोग करने पर दर्दनाक है, इसलिए आपको उस विशेषता को अनदेखा कर देना चाहिए, जब charsetकंटेंट-टाइप हेडर में कोई सेट नहीं है।


क्या bs4 के लिए StrainedSoup जैसा कुछ है? (मुझे अब इसकी आवश्यकता नहीं है, लेकिन सिर्फ सोच रहा है, अगर आप चाहते हैं कि आप इसे जोड़
सकें


क्या कोई कारण है कि यह कोड ब्यूटीफुल कंस्ट्रक्टर को "फीचर =" पास नहीं करता है? BeautifulSoup मुझे डिफ़ॉल्ट पार्सर का उपयोग करने के बारे में चेतावनी देता है।
माइक

1
@ माइक: जब मैंने यह उत्तर लिखा था सुंदरसुपे ने अभी तक चेतावनी नहीं दी है यदि आपने नहीं किया।
मार्टिन पीटर्स

50

दूसरों ने ब्यूटीफुल की सिफारिश की है, लेकिन यह एलएक्सएमएल का उपयोग करना बेहतर है । अपने नाम के बावजूद, यह HTML को पार्स और स्क्रैप करने के लिए भी है। यह सुंदरसुपर की तुलना में बहुत तेज है, और यह सुंदरसुपर (उनकी प्रसिद्धि के दावे) की तुलना में बेहतर "टूटे हुए" HTML को भी संभालता है। अगर आप lxml API सीखना नहीं चाहते हैं तो भी BeautifulSoup के लिए कम्पैटिबिलिटी API है।

इयान ब्लकिंग सहमत हैं

जब तक आप Google App Engine पर नहीं होते हैं या ऐसा कुछ भी नहीं होता है, जहां BeautifulSoup का उपयोग करने का कोई कारण नहीं है, तो कुछ भी नहीं है जो कि विशुद्ध रूप से अजगर की अनुमति नहीं है।

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

23
lxmlअगर स्थापित किया गया है तो सुंदर 4 डिफ़ॉल्ट डिफ़ॉल्ट पार्सर के रूप में उपयोग करेगा ।
मार्टिन पीटर्स

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

इससे मेरे कोड के साथ एक समस्या हल हो गई। धन्यवाद!
आरजे

10

निम्नलिखित कोड का उपयोग करके वेबपेज में उपलब्ध सभी लिंक को पुनः प्राप्त करना है 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'))

8

हुड ब्यूटीफुलसप के तहत अब 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 सूची को साफ़ करने का एक सरल तरीका है।


1
अगर यह एक रीपोस्ट है, तो मूल पोस्ट में शामिल क्यों नहीं है: 1. अनुरोध 2. सूची COMP 3. तर्क साइट आंतरिक और रद्दी लिंक को साफ़ करने के लिए ?? कोशिश करो और दो पदों के परिणामों की तुलना करें, मेरी सूची COMP एक आश्चर्यजनक रूप से अच्छा काम करता है रद्दी लिंक स्क्रबिंग।
23

ओपी ने उन सुविधाओं के लिए नहीं पूछा और जिस हिस्से के लिए उन्होंने पूछा वह पहले से ही पोस्ट किए गए सटीक तरीके का उपयोग करके पोस्ट और हल किया गया है। हालाँकि, मैं डाउनवोट को हटा दूंगा क्योंकि सूची की समझ उन लोगों के लिए मूल्य जोड़ती है जो उन सुविधाओं को चाहते हैं और आप पोस्ट के शरीर में स्पष्ट रूप से उनका उल्लेख करते हैं। इसके अलावा, आप प्रतिनिधि का उपयोग कर सकते हैं :)
dotancohen

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]

अधिक जटिल परिचालनों के लिए, बेशक बीएसओपी को अभी भी पसंद किया जाता है।


7
और अगर, उदाहरण के लिए, वहाँ कुछ inbetween <aऔर है href? कहो rel="nofollow"या onclick="..."सिर्फ एक नई लाइन? stackoverflow.com/questions/1732348/…
dimo414

क्या इसके साथ केवल कुछ लिंक को फ़िल्टर करने का एक तरीका है? जैसे मैं कहता हूं कि मैं केवल उन लिंक चाहता हूं जिनके लिंक में "एपिसोड" है?
nwgat

4

यह स्क्रिप्ट वही करती है जो आपकी तलाश में है, लेकिन निरपेक्ष लिंक के सापेक्ष लिंक को भी हल करता है।

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

यह वह नहीं करता है जो करने का मतलब है; यदि resolution_links () में रूट नहीं है, तो यह कभी भी कोई URL नहीं लौटाता है।
माइकबी

4

सभी लिंक खोजने के लिए, हम इस उदाहरण में re.module * के साथ urllib2 मॉड्यूल का उपयोग करेंगे * पुन: मॉड्यूल में सबसे शक्तिशाली फ़ंक्शन में से एक "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

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

1
मैं इसे समझने में सक्षम होना पसंद करूँगा, जहाँ मैं कुशलता से (r"<a.*?\s*href=\"(.*?)\".*?>(.*?)</a>", page)इसका मतलब निकाल सकता हूँ ? धन्यवाद!
user1063287

9
वास्तव में एक बुरा विचार है। हर जगह टूटा हुआ HTML।
उफोगुय

2
HTML को पार्स करने के लिए नियमित अभिव्यक्तियों का उपयोग क्यों न करें: stackoverflow.com/questions/1732348/…
10

@ user1063287, वेब regex ट्यूटोरियल्स से भरा है। यह एक जोड़े को पढ़ने के लिए आपके समय के लायक है। जबकि आरईएस वास्तव में जटिल हो सकता है, जिस बारे में आप पूछ रहे हैं वह बहुत बुनियादी है।
एलेक्सिस

3

लिंक विभिन्न प्रकार की विशेषताओं के भीतर हो सकते हैं ताकि आप उन विशेषताओं की सूची का चयन कर सकें

उदाहरण के लिए, 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 जिसका मान मूल्य से पहले (उपसर्ग) है।


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

मुझे निम्नलिखित सुधार के बाद @ 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)

पायथन 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 url पार्सिंग लोडिंग को हैंडल नहीं करता है 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: मुझे लगता lxml.htmlहै कि की तुलना में थोड़ा अधिक उदार है lxml.etree। यदि आपका इनपुट अच्छी तरह से नहीं बना है, तो आप स्पष्ट रूप से BeautifulSoup parser: lxml.de/elements.p.html सेट कर सकते हैं । और अगर आप बीटफुलसैप के साथ जाते हैं तो बीएस 3 बेहतर विकल्प है।
ccpindra

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

0

बाहरी और आंतरिक दोनों लिंक के साथ एक साथ कई डुप्लिकेट लिंक हो सकते हैं। दोनों के बीच अंतर करने के लिए और बस सेट का उपयोग करके अद्वितीय लिंक प्राप्त करें:

# 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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.