ฉันจะดึงลิงค์ของหน้าเว็บและคัดลอกที่อยู่ url ของลิงค์โดยใช้ Python ได้อย่างไร
ฉันจะดึงลิงค์ของหน้าเว็บและคัดลอกที่อยู่ url ของลิงค์โดยใช้ Python ได้อย่างไร
คำตอบ:
นี่เป็นตัวอย่างสั้น ๆ ที่ใช้คลาส SoupStrainer ใน 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'])
จริง ๆ แล้วเอกสาร BeautifulSoup ค่อนข้างดีและครอบคลุมสถานการณ์ทั่วไปหลายประการ:
https://www.crummy.com/software/BeautifulSoup/bs4/doc/
แก้ไข: โปรดทราบว่าฉันใช้คลาส SoupStrainer เพราะมันมีประสิทธิภาพมากกว่าเล็กน้อย (หน่วยความจำและความเร็วฉลาด) ถ้าคุณรู้ว่าคุณกำลังแยกวิเคราะห์ล่วงหน้า
/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."
เพื่อความสมบูรณ์แบบเวอร์ชั่น 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'])
หรือเวอร์ชัน 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']
และเวอร์ชันที่ใช้requests
ไลบรารีซึ่งตามที่เขียนจะใช้ได้ทั้ง Python 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
แอตทริบิวต์ องค์ประกอบที่ไม่มีแอตทริบิวต์จะถูกข้ามไป
BeautifulSoup 3 หยุดพัฒนาในเดือนมีนาคม 2012 โครงการใหม่ควรใช้ BeautifulSoup 4 เสมอ
โปรดทราบว่าคุณควรจะออกจากการถอดรหัส HTML จากไบต์เพื่อ BeautifulSoup คุณสามารถแจ้ง BeautifulSoup ของชุดอักขระที่พบในส่วนหัวการตอบกลับ HTTP เพื่อช่วยในการถอดรหัส แต่อาจผิดและขัดแย้งกับ<meta>
ข้อมูลส่วนหัวที่พบใน HTML เองซึ่งเป็นสาเหตุที่ใช้วิธีการเรียนภายใน BeautifulSoup EncodingDetector.find_declared_encoding()
เพื่อให้แน่ใจว่า คำแนะนำการเข้ารหัสแบบฝังดังกล่าวจะชนะเหนือเซิร์ฟเวอร์ที่กำหนดค่าผิดพลาด
ด้วยความrequests
ที่response.encoding
เป็นค่าเริ่มต้นแอตทริบิวต์ละติน-1 ถ้าการตอบสนองที่มีtext/*
ชนิด mime แม้ว่าจะไม่มี characterset ถูกส่งกลับ สิ่งนี้สอดคล้องกับ HTTP RFCs แต่เจ็บปวดเมื่อใช้กับการแยกวิเคราะห์ HTML ดังนั้นคุณควรละเว้นแอตทริบิวต์นั้นเมื่อไม่มีการcharset
ตั้งค่าในส่วนหัวของประเภทเนื้อหา
อื่น ๆ ได้แนะนำ BeautifulSoup แต่มันเป็นเรื่องที่ดีมากกับการใช้lxml แม้จะมีชื่อมันก็ยังสำหรับการแยกและขูด HTML มันเร็วกว่า BeautifulSoup มากและมันยังจัดการ HTML ที่ "เสียหาย" ได้ดีกว่า BeautifulSoup (พวกเขาอ้างสิทธิ์ในชื่อเสียง) มันมี API ที่เข้ากันได้สำหรับ BeautifulSoup ด้วยหากคุณไม่ต้องการเรียนรู้ lxml API
เอียน Blicking ตกลง
ไม่มีเหตุผลที่จะใช้ BeautifulSoup อีกต่อไปเว้นแต่คุณจะใช้ Google App Engine หรือบางสิ่งที่ไม่อนุญาตให้ใช้ Python อย่างแท้จริง
lxml.html ยังรองรับ CSS3 selectors ดังนั้นสิ่งเล็ก ๆ น้อย ๆ เช่นนี้
ตัวอย่างที่มี 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
lxml
เป็นตัวแยกวิเคราะห์เริ่มต้นหากติดตั้ง
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]
ในรายการคอมพ์ "if '//' และ 'url.com' ไม่ได้อยู่ใน x" เป็นวิธีการง่าย ๆ ในการขัดรายการ URL ของ URL การนาวิเกต 'ภายใน' ของไซต์เป็นต้น
เพียงเพื่อรับลิงก์โดยไม่ต้อง B.soup และ 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]
สำหรับการดำเนินการที่ซับซ้อนยิ่งขึ้นแน่นอนว่า BSoup ยังคงเป็นที่ต้องการ
<a
และhref
? พูดrel="nofollow"
หรือonclick="..."
หรือเพียงแค่ขึ้นบรรทัดใหม่? stackoverflow.com/questions/1732348/…
สคริปต์นี้ทำในสิ่งที่คุณต้องการ แต่ยังแก้ไขลิงก์ที่เกี่ยวข้องไปยังลิงก์แบบสัมบูรณ์
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
ในการค้นหาลิงก์ทั้งหมดในตัวอย่างนี้เราจะใช้โมดูล 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
ทำไมไม่ใช้การแสดงออกปกติ:
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)
ที่ไหน ขอบคุณ!
ลิงก์สามารถอยู่ในแอตทริบิวต์ที่หลากหลายเพื่อให้คุณสามารถส่งรายการของแอตทริบิวต์เหล่านั้นเพื่อเลือก
ตัวอย่างเช่นด้วยแอตทริบิวต์ 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)
สำหรับ Python 3:
urllib.parse.urljoin
จะต้องใช้เพื่อให้ได้ URL แบบเต็มแทน
ตัวแยกวิเคราะห์ของ BeatifulSoup สามารถช้า อาจเป็นไปได้มากกว่าที่จะใช้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
อย่างไร
lxml.html
lxml.etree
หากอินพุตของคุณไม่ได้อยู่ในรูปแบบที่ดีแล้วคุณสามารถตั้งค่าตัวแยกวิเคราะห์ BeautifulSoup: lxml.de/elementsoup.htmlอย่างชัดเจน และถ้าคุณไปกับ BeatifulSoup BS3 เป็นตัวเลือกที่ดีกว่า
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']
อาจมีลิงค์ที่ซ้ำกันจำนวนมากพร้อมกับลิงค์ทั้งภายนอกและภายใน หากต้องการแยกความแตกต่างระหว่างทั้งสองและเพิ่งได้รับลิงก์ที่ไม่ซ้ำใครโดยใช้ชุด:
# 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)