استرداد الروابط من صفحة الويب باستخدام python وBeautifulSoup

StackOverflow https://stackoverflow.com/questions/1080411

سؤال

كيف يمكنني استرداد روابط صفحة الويب ونسخ عنوان URL للروابط باستخدام Python؟

هل كانت مفيدة؟

المحلول

فيما يلي مقتطف قصير باستخدام فئة SoupStrainer في BeautifulSoup:

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

يحرر:لاحظ أنني استخدمت فئة SoupStrainer لأنها أكثر كفاءة قليلاً (الذاكرة والسرعة)، إذا كنت تعرف ما تقوم بتحليله مسبقًا.

نصائح أخرى

لأجل اكتمال، وإصدار 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']

وأو النسخة بيثون 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 مكتبة، والتي كما هو مكتوب ستعمل في كل من بايثون 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'])

والدعوة soup.find_all('a', href=True) تجد جميع العناصر <a> التي لها سمة href. العناصر بدون السمة يتم تخطي.

وBeautifulSoup 3 توقفت التنمية مارس 2012. مشاريع جديدة حقا يجب أن تستخدم BeautifulSoup 4، دائما.

ملحوظة أنه يجب ترك فك رموز HTML من بايت <م> إلى BeautifulSoup . يمكنك إبلاغ BeautifulSoup من characterset وجدت في رؤوس استجابة HTTP للمساعدة في فك التشفير، ولكن هذا <م> يمكن من الخطأ ومتضاربة مع معلومات رأس <meta> وجدت في HTML نفسه، والذي هو السبب في أن يستخدم فوق BeautifulSoup الداخلية أسلوب فئة EncodingDetector.find_declared_encoding() للتأكد من أن هذه التلميحات ترميز جزءا لا يتجزأ من الفوز على الخادم تكوينه بشكل خاطئ.

ومع requests، وresponse.encoding السمة افتراضات إلى اللاتينية-1 إذا كانت الاستجابة لها MIMETYPE text/*، حتى لو كان إرجاع أية characterset. وهذا يتفق مع المراجع HTTP ولكن المؤلم عندما تستخدم مع الاعراب HTML، لذا يجب عليك تجاهل هذه السمة عند تعيين أي charset في رأس نوع المحتوى.

وقد أوصى آخرون بـ BeautifulSoup، لكنه أفضل بكثير للاستخدام com.lxml.على الرغم من اسمه، فهو أيضًا مخصص لتحليل وتجميع HTML.إنه أسرع بكثير من BeautifulSoup، كما أنه يتعامل مع HTML "المعطل" بشكل أفضل من BeautifulSoup (الذي يزعمون أنهم مشهورون).يحتوي على واجهة برمجة تطبيقات متوافقة مع BeautifulSoup أيضًا إذا كنت لا ترغب في تعلم واجهة برمجة تطبيقات 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
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]

في قائمة شركات، و "إذا" // "و" url.com "ليس في x" هو طريقة بسيطة لتنظيف قائمة عنوان الموقع من عناوين المواقع والملاحة "الداخلية" المواقع، الخ.

للعثور على جميع الروابط ، سنستخدم في هذا المثال وحدة 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

وفقط من أجل الحصول على وصلات، دون B.soup والتعابير المنطقية:

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 لا يزال يفضل.

لماذا لا تستخدم التعابير العادية:

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

وهذا السيناريو يفعل ما تبحث عن، ولكن أيضا حل الارتباطات النسبية إلى ارتباطات مطلقة.

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

روابط يمكن أن يكون ضمن مجموعة متنوعة من سمات ذلك يمكن أن تمر على قائمة من هذه الصفات لتحديد

وعلى سبيل المثال، مع 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)

لبيثون 3:

وurllib.parse.urljoin تمت لاستخدامها من أجل الحصول على URL الكامل بدلا من ذلك.

يمكن أن يكون المحلل اللغوي الخاص بـ BeatifulSoup بطيئًا.قد يكون أكثر جدوى للاستخدام com.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"
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']
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top