পাইথন এবং বিউটিফুলসুপ ব্যবহার করে ওয়েব পৃষ্ঠা থেকে লিঙ্কগুলি পুনরুদ্ধার করুন


141

আমি কীভাবে কোনও ওয়েবপৃষ্ঠার লিঙ্কগুলি পুনরুদ্ধার করতে এবং পাইথন ব্যবহার করে লিঙ্কগুলির url ঠিকানাটি অনুলিপি করতে পারি?

উত্তর:


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, স্যুপ স্ট্রেনার ব্যবহার করা একটি দুর্দান্ত ধারণা কারণ এটি যখন আপনার সমস্ত লিঙ্ক হয় তখন আপনাকে অনেক অযথা পার্সিংয়ের সুযোগ দেয় vent
ইভান ফসমার্ক

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."
বেনডুন্ডি

27
BeautifulSoup সংস্করণ 3.2.1 উপর সেখানে নেই has_attr। পরিবর্তে আমি দেখি এখানে কিছু আছে has_keyএবং এটি কাজ করে।

2
Python3 জন্য আপডেট
John Doe

7
বিএস 4 আমদানি করা বিউটিফুলসপ থেকে। (বিউটিফুলসপ ইম্পোর্ট বিউটিফুলসুপ থেকে নয় ..) সংশোধন প্রয়োজন।
habষভ অগ্রহরি

67

সম্পূর্ণতার জন্য, বিউটিফুলসপ 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 ব্যবহার করা উচিত।

নোট করুন যে আপনার এইচটিএমএলকে বাইট থেকে বিউটিফুলসুপে ডিকোডিং করা উচিত । আপনি characterset HTTP প্রতিক্রিয়া হেডার পাওয়া ডিকোডিং সহায়তা করার BeautifulSoup অবহিত করতে পারেন, কিন্তু এই করতে ভুল এবং একটি সঙ্গে পরস্পরবিরোধী হতে <meta>হেডারের তথ্য এইচটিএমএল নিজেই পাওয়া যায় নি, যে কারণে উপরে ব্যবহারসমূহ BeautifulSoup অভ্যন্তরীণ বর্গ পদ্ধতিEncodingDetector.find_declared_encoding() নিশ্চিত করতে যেমন এম্বেড থাকা এনকোডিং ইঙ্গিতগুলি একটি ভুল কনফিগার্ড সার্ভারের উপরে জয়ী হয়।

সাথে requests, response.encodingপ্রতিক্রিয়ার একটি text/*মাইমটাইপ থাকলে ল্যাটিন -১ এ অ্যাট্রিবিউটটি ডিফল্ট হয় , এমনকি কোনও অক্ষর ফেরানো না হলেও। এটি এইচটিটিপি আরএফসিগুলির সাথে সামঞ্জস্যপূর্ণ তবে HTML পার্সিংয়ের সাথে ব্যবহারের সময় বেদনাদায়ক তাই charsetকন্টেন্ট-টাইপ শিরোনামে কোনও সেট না করা অবস্থায় আপনার সেই বৈশিষ্ট্যটিকে উপেক্ষা করা উচিত ।


বিএস 4 এর জন্য স্ট্রেইনডসপের মতো কিছু আছে কি? (আমার এখনই এটির দরকার নেই তবে কেবল অবাক হয়ে ভাবছেন, সেখানে যদি আপনি এটি যোগ করতে চান তবে)
অ্যান্টি হাপাল


এই কোডটি বিউটিউসসুপ কনস্ট্রাক্টরের কাছে "বৈশিষ্ট্যগুলি =" না দেওয়ার কি কারণ আছে? বিউটিউসসপ আমাকে ডিফল্ট পার্সার ব্যবহার সম্পর্কে একটি সতর্কতা দেয়।
মাইকিব

1
@ মাইকিবি: যখন আমি এই উত্তরটি লিখেছি বিউটিফুলসপ এখনও কোনও সতর্কতা উত্থাপন করেনি যদি আপনি না করেন।
মার্টিজন পিটারস

50

অন্যরা বিউটিফুলসুপের প্রস্তাব দিয়েছেন, তবে lxML ব্যবহার করা আরও ভাল । এর নাম সত্ত্বেও এটি এইচটিএমএল বিশ্লেষণ এবং স্ক্র্যাপ করার জন্য is এটি বিউটিফুলসাপের চেয়ে অনেক দ্রুত এবং এটি এমনকি "ভাঙ্গা" এইচটিএমএলকে বিউটিফুলসাপের (তাদের খ্যাতির দাবি) থেকেও ভাল পরিচালনা করে। আপনি lxML এপিআই শিখতে না চাইলে এটি বিউটিফুলসুপের জন্য একটি সামঞ্জস্যতা এপিআই রয়েছে

ইয়ান ক্লিক করা একমত

বিউটিফুলসপ ব্যবহার করার আর কোনও কারণ নেই, যদি না আপনি গুগল অ্যাপ ইঞ্জিনে বা এমন কিছু হন যেখানে নিখুঁতভাবে পাইথনের অনুমতি নেই।

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
বিউটিফুলসপ 4 lxmlইনস্টল করা থাকলে ডিফল্ট পার্সার হিসাবে ব্যবহার করবে ।
মার্টিজন পিটারস

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]

তালিকার কমপ্লেক্সে, "if '//' এবং 'url.com' x নয়," সাইটের 'অভ্যন্তরীণ' নেভিগেশন ইউআরএল ইত্যাদির url তালিকা স্ক্রাব করার একটি সহজ পদ্ধতি method


1
যদি এটি কোনও পোস্ট পোস্ট হয় তবে মূল পোস্টটি কেন অন্তর্ভুক্ত নয়: ১. অনুরোধ ২.লিস্ট কমপ ৩. সাইটটি অভ্যন্তরীণ ও জাঙ্ক লিঙ্কগুলি স্ক্রাব করার জন্য যুক্তি ?? চেষ্টা করুন এবং দুটি পোস্টের ফলাফলের সাথে তুলনা করুন, আমার লিস্ট কমপটি আশ্চর্যজনকভাবে ভাল কাজ করে যাতে জাঙ্ক লিঙ্কগুলি ঝাঁঝরা হয়।
cheekybastard

ওপি সেই বৈশিষ্ট্যগুলির জন্য জিজ্ঞাসা করেনি এবং তিনি যে অংশটি চেয়েছিলেন তা ইতিমধ্যে আপনার পোস্টের মতো সঠিক পদ্ধতি ব্যবহার করে পোস্ট করা হয়েছে এবং সমাধান করা হয়েছে। যাইহোক, আমি তালিকাটি বোধগম্য হওয়ার কারণে ডাউনভোটটি সরিয়ে ফেলব যা এই বৈশিষ্ট্যগুলি চায় এমন লোকদের জন্য মূল্য যুক্ত করে এবং আপনি সেগুলি স্পষ্টভাবে পোস্টের শিরোনামে উল্লেখ করেন। এছাড়াও, আপনি রেপ ব্যবহার করতে পারেন :)
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
এবং যদি, উদাহরণস্বরূপ, অন্তর্নির্মিত <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

এটি টিআই যা করতে চেয়েছিল তা করে না; যদি দ্রষ্টব্য_লিঙ্কগুলি () এর মূল না থাকে তবে তা কখনই কোনও ইউআরএল ফেরায় না।
মাইকিব

4

সমস্ত লিঙ্কগুলি সন্ধানের জন্য, আমরা এই উদাহরণে পুনরায় মডুলের সাথে urllib2 মডিউলটি ব্যবহার করব * রে মডিউলের অন্যতম শক্তিশালী ফাংশন হ'ল "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)অর্থ খুঁজে পেতে পারি ? ধন্যবাদ!
ব্যবহারকারী 1063287

9
সত্যিই একটি খারাপ ধারণা। সর্বত্র ভাঙা এইচটিএমএল।
উফোগুয়

2
কেন নিয়মিত এক্সপ্রেশন ব্যবহার করবেন পার্স এইচটিএমএল: stackoverflow.com/questions/1732348/...
allcaps

@ user1063287, ওয়েবটি রেগেক্স টিউটোরিয়াল পূর্ণ। একটি দম্পতি পড়া আপনার সময় ভাল। আরইএস সত্যিই সংশ্লেষিত হয়ে উঠতে পারে, আপনি যার সম্পর্কে জিজ্ঞাসা করছেন এটি বেশ বেসিক।
অ্যালেক্সিস

3

লিঙ্কগুলি বিভিন্ন বৈশিষ্ট্যের মধ্যে থাকতে পারে যাতে আপনি নির্বাচন করতে সেই বৈশিষ্ট্যগুলির একটি তালিকা পাস করতে পারেন

উদাহরণস্বরূপ, src এবং href অ্যাট্রিবিউট (এখানে আমি ^ অপারেটর দিয়ে শুরুটি ব্যবহার করছি এটি নির্দিষ্ট করার জন্য যে এই বৈশিষ্ট্যগুলির মানগুলির কোনওটি http দিয়ে শুরু হয় required আপনি এটি প্রয়োজনীয় হিসাবে উপযুক্ত করতে পারেন

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 ^ = VALUE]

অ্যাটারের একটি গুণবাচক নাম সহ এমন উপাদানগুলিকে পুনঃস্থাপন করে যার মান দ্বারা মান পূর্বনির্ধারিত (পূর্ববর্তী) হয়।


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

নীচের সংশোধন করার পরে @ ব্লেয়ারগ 23 কাজ করে আমি উত্তর পেয়েছি (যেখানে এটি সঠিকভাবে কাজ করতে ব্যর্থ হয়েছে সেই দৃশ্যের আবরণ):

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 ব্যবহার করা আরও সম্ভাব্য হতে পারে যা ইউআরএল থেকে সরাসরি পার্স করতে সক্ষম (নীচে বর্ণিত কিছু সীমাবদ্ধতা সহ)।

import lxml.html

doc = lxml.html.parse(url)

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

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

উপরের কোডগুলি লিঙ্কগুলি যেমন আছে তেমন ফিরিয়ে দেবে এবং বেশিরভাগ ক্ষেত্রে তারা সাইটের মূল থেকে আপেক্ষিক লিঙ্ক বা পরম হবে। যেহেতু আমার ব্যবহারের ক্ষেত্রে কেবলমাত্র একটি নির্দিষ্ট ধরণের লিঙ্কগুলিই বের করা ছিল, নীচে এমন একটি সংস্করণ যা লিঙ্কগুলিকে পুরো ইউআরএলগুলিতে রূপান্তর করে এবং যা বৈকল্পিকভাবে একটি গ্লোব প্যাটার্ন গ্রহণ করে *.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?
অ্যালেক্সিস

@ অ্যালেক্সিস: আমি মনে করি এটির lxml.htmlচেয়ে কিছুটা বেশি হালকা lxml.etree। যদি আপনার ইনপুটটি সুগঠিত না হয় তবে আপনি স্পষ্টতই বিউটিফুলসুফ পার্সার সেট করতে পারেন: lxML.de/eitionsoup.html । এবং আপনি যদি বিটিফুলসপ দিয়ে যান তবে বিএস 3 আরও ভাল পছন্দ।
সিসিপিজ্জা

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)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.