ब्यूटीफुल ग्रूप दर्शनीय वेबपेज टेक्स्ट


124

मूल रूप से, मैं एक वेबपेज पर दृश्यमान पाठ को सख्ती से हथियाने के लिए ब्यूटीफुल का उपयोग करना चाहता हूं । उदाहरण के लिए, यह वेबपृष्ठ मेरा परीक्षण मामला है। और मैं मुख्य रूप से केवल शरीर पाठ (लेख) प्राप्त करना चाहता हूं और शायद यहां और वहां कुछ टैब नाम भी। मैंने इस SO प्रश्न में सुझाव देने की कोशिश की है जिसमें बहुत सारे <script>टैग और HTML टिप्पणियाँ हैं जो मुझे नहीं चाहिए। मैं findAll()केवल वेबपृष्ठ पर दृश्यमान पाठ प्राप्त करने के लिए फ़ंक्शन के लिए आवश्यक तर्कों का पता नहीं लगा सकता ।

इसलिए, मुझे लिपियों, टिप्पणियों, सीएसएस आदि को छोड़कर सभी दृश्यमान पाठ कैसे खोजने चाहिए?

जवाबों:


239

इसे इस्तेमाल करे:

from bs4 import BeautifulSoup
from bs4.element import Comment
import urllib.request


def tag_visible(element):
    if element.parent.name in ['style', 'script', 'head', 'title', 'meta', '[document]']:
        return False
    if isinstance(element, Comment):
        return False
    return True


def text_from_html(body):
    soup = BeautifulSoup(body, 'html.parser')
    texts = soup.findAll(text=True)
    visible_texts = filter(tag_visible, texts)  
    return u" ".join(t.strip() for t in visible_texts)

html = urllib.request.urlopen('http://www.nytimes.com/2009/12/21/us/21storm.html').read()
print(text_from_html(html))

47
+1 soup.findAll(text=True)उस सुविधा के बारे में कभी नहीं जानते थे
हार्टले ब्रॉडी

7
हाल के बीएस 4 (कम से कम) के लिए आप isinstance(element, Comment)एक रेगेक्स के साथ मिलान के बजाय टिप्पणियों की पहचान कर सकते हैं ।
ट्रिपल

5
मेरा मानना ​​है कि लाइन २ होनी चाहिएsoup = BeautifulSoup(html)
४cz४ जुलाई को ४४ ४४

11
दृश्यमान कार्य में, टिप्पणियों को खोजने के लिए एलिफ काम नहीं करता था। मुझे इसे अपडेट करना था elif isinstance(element,bs4.element.Comment):। मैंने माता-पिता की सूची में 'मेटा' भी जोड़ा।
रस सैवेज

4
उपरोक्त फ़िल्टर के परिणाम में बहुत सारे \ n हैं, सफेद रिक्त स्थान और नई लाइनों को खत्म करने के लिए निम्न कोड जोड़ें:elif re.match(r"[\s\r\n]+",str(element)): return False
07

37

@Jbochi से स्वीकृत उत्तर मेरे लिए काम नहीं करता है। Str () फंक्शन कॉल एक अपवाद को जन्म देती है क्योंकि यह ब्यूटीफुल तत्व में गैर-एस्की अक्षर को एनकोड नहीं कर सकती है। यहां उदाहरण वेब पेज को दृश्यमान टेक्स्ट को फ़िल्टर करने का एक अधिक उपयुक्त तरीका है।

html = open('21storm.html').read()
soup = BeautifulSoup(html)
[s.extract() for s in soup(['style', 'script', '[document]', 'head', 'title'])]
visible_text = soup.getText()

1
यदि str(element)एन्कोडिंग समस्याओं के साथ विफल रहता है, unicode(element)तो आपको इसके बजाय प्रयास करना चाहिए यदि आप पायथन 2 का उपयोग कर रहे हैं
mknaf

31
import urllib
from bs4 import BeautifulSoup

url = "https://www.yahoo.com"
html = urllib.urlopen(url).read()
soup = BeautifulSoup(html)

# kill all script and style elements
for script in soup(["script", "style"]):
    script.extract()    # rip it out

# get text
text = soup.get_text()

# break into lines and remove leading and trailing space on each
lines = (line.strip() for line in text.splitlines())
# break multi-headlines into a line each
chunks = (phrase.strip() for line in lines for phrase in line.split("  "))
# drop blank lines
text = '\n'.join(chunk for chunk in chunks if chunk)

print(text.encode('utf-8'))

4
पिछले उत्तर मेरे काम नहीं आए, लेकिन इसने :)
rjurney

अगर मैं url imfuna.com पर यह कोशिश करता हूं तो यह केवल 6 शब्द (Imfuna संपत्ति इन्वेंटरी और निरीक्षण ऐप्स) देता है, इस तथ्य के बावजूद कि पृष्ठ पर बहुत अधिक पाठ / शब्द हैं ... कोई भी विचार क्यों यह उत्तर उस के लिए काम नहीं करता है यूआरएल? @ बम्पकिन
the_t_test_1

10

मैं प्रस्तुत सामग्री प्राप्त करने के लिए सुंदर सूप का उपयोग करते हुए पूरी तरह से सम्मान करता हूं, लेकिन यह एक पृष्ठ पर प्रदान की गई सामग्री को प्राप्त करने के लिए आदर्श पैकेज नहीं हो सकता है।

मुझे प्रदान की गई सामग्री, या विशिष्ट ब्राउज़र में दृश्यमान सामग्री प्राप्त करने के लिए एक समान समस्या थी। विशेष रूप से मेरे पास ऐसे कई उदाहरण हैं जो नीचे दिए गए ऐसे सरल उदाहरण के साथ काम करते हैं। इस मामले में गैर-प्रदर्शन योग्य टैग एक शैली टैग में नेस्टेड है, और कई ब्राउज़रों में दिखाई नहीं देता है जिन्हें मैंने जांचा है। अन्य विविधताएं मौजूद हैं जैसे कि क्लास टैग सेटिंग डिस्प्ले को कोई भी परिभाषित नहीं करता है। फिर div के लिए इस वर्ग का उपयोग करना।

<html>
  <title>  Title here</title>

  <body>

    lots of text here <p> <br>
    <h1> even headings </h1>

    <style type="text/css"> 
        <div > this will not be visible </div> 
    </style>


  </body>

</html>

ऊपर पोस्ट किया गया एक समाधान है:

html = Utilities.ReadFile('simple.html')
soup = BeautifulSoup.BeautifulSoup(html)
texts = soup.findAll(text=True)
visible_texts = filter(visible, texts)
print(visible_texts)


[u'\n', u'\n', u'\n\n        lots of text here ', u' ', u'\n', u' even headings ', u'\n', u' this will not be visible ', u'\n', u'\n']

इस समाधान में निश्चित रूप से कई मामलों में एप्लिकेशन हैं और यह काम आम तौर पर काफी अच्छी तरह से करता है लेकिन ऊपर पोस्ट किए गए HTML में यह उस पाठ को बरकरार रखता है जिसका प्रतिपादन नहीं किया गया है। SO की खोज करने के बाद एक युगल समाधान यहाँ आया है सुंदरसोते get_text सभी टैग और जावास्क्रिप्ट को नहीं हटाता है और यहाँ पर HTML को सादे पाठ में पायथन का उपयोग करके रेंडर किया गया है

मैंने इन दोनों समाधानों की कोशिश की: html2text और nltk.clean_html और समय के परिणामों से आश्चर्यचकित हो गया था ताकि उन्हें लगा कि उन्होंने उत्तर देने के लिए उत्तर दिया। बेशक, गति अत्यधिक डेटा की सामग्री पर निर्भर करती है ...

यहाँ @Helge से एक उत्तर सभी चीजों के nltk का उपयोग करने के बारे में था।

import nltk

%timeit nltk.clean_html(html)
was returning 153 us per loop

यह प्रदान की HTML के साथ एक स्ट्रिंग लौटने के लिए वास्तव में अच्छी तरह से काम किया। यह nltk मॉड्यूल html2text से भी तेज था, हालांकि शायद html2text अधिक मजबूत है।

betterHTML = html.decode(errors='ignore')
%timeit html2text.html2text(betterHTML)
%3.09 ms per loop

3

यदि आप प्रदर्शन की परवाह करते हैं, तो यहां एक और अधिक कुशल तरीका है:

import re

INVISIBLE_ELEMS = ('style', 'script', 'head', 'title')
RE_SPACES = re.compile(r'\s{3,}')

def visible_texts(soup):
    """ get visible text from a document """
    text = ' '.join([
        s for s in soup.strings
        if s.parent.name not in INVISIBLE_ELEMS
    ])
    # collapse multiple spaces to two spaces.
    return RE_SPACES.sub('  ', text)

soup.stringsएक पुनरावृत्तिकर्ता है, और यह वापस लौटता है NavigableStringताकि आप कई लूपों के बिना माता-पिता के टैग नाम को सीधे देख सकें।


2

शीर्षक एक <nyt_headline>टैग के अंदर है , जो एक <h1>टैग और <div>आईडी "लेख" के साथ टैग के अंदर निहित है ।

soup.findAll('nyt_headline', limit=1)

कार्य करना चाहिए।

लेख निकाय एक <nyt_text>टैग के अंदर है , जो <div>आईडी "लेखबॉडी" के साथ एक टैग के अंदर निहित है। <nyt_text> तत्व के अंदर , पाठ स्वयं <p> टैग के भीतर समाहित है । छवियाँ उन <p>टैग के भीतर नहीं हैं । सिंटेक्स के साथ प्रयोग करना मेरे लिए मुश्किल है, लेकिन मुझे उम्मीद है कि कुछ इस तरह से काम कर सकूंगा।

text = soup.findAll('nyt_text', limit=1)[0]
text.findAll('p')

मुझे यकीन है कि यह इस परीक्षण के मामले के लिए काम करता है, हालांकि, अधिक सामान्य उत्तर की तलाश में है जो विभिन्न अन्य वेबसाइटों पर लागू हो सकता है ... अब तक, मैंने <स्क्रिप्ट> </ script> टैग और <खोजने के लिए regexps का उपयोग करने की कोशिश की है। ! -! * -> टिप्पणियां और उन्हें "" से बदलें लेकिन यह सम राशि के लिए थोड़े मुश्किल साबित हो रहा है ..
user233864

2

हालांकि, मैं पूरी तरह से सामान्य रूप से सुंदर-सूप का उपयोग करने का सुझाव दूंगा, अगर कोई भी एक विकृत HTML के दृश्य भागों को प्रदर्शित करने के लिए देख रहा है (जैसे कि आपके पास जहां भी कारण के लिए एक वेब-पेज का सिर्फ एक खंड या रेखा है), तो निम्नलिखित <और >टैग के बीच की सामग्री को हटा देगा :

import re   ## only use with malformed html - this is not efficient
def display_visible_html_using_re(text):             
    return(re.sub("(\<.*?\>)", "",text))

2

खाली लाइनों और बकवास के बिना, स्ट्रिंग्स प्राप्त करने के लिए कम कोड के साथ सबसे आसान तरीका ब्यूटीफुल का उपयोग करना।

tag = <Parent_Tag_that_contains_the_data>
soup = BeautifulSoup(tag, 'html.parser')

for i in soup.stripped_strings:
    print repr(i)

0

इस मामले को संभालने का सबसे सरल तरीका है getattr()। आप अपनी आवश्यकताओं के लिए इस उदाहरण को अनुकूलित कर सकते हैं:

from bs4 import BeautifulSoup

source_html = """
<span class="ratingsDisplay">
    <a class="ratingNumber" href="https://www.youtube.com/watch?v=oHg5SJYRHA0" target="_blank" rel="noopener">
        <span class="ratingsContent">3.7</span>
    </a>
</span>
"""

soup = BeautifulSoup(source_html, "lxml")
my_ratings = getattr(soup.find('span', {"class": "ratingsContent"}), "text", None)
print(my_ratings)

यह टेक्स्ट एलीमेंट "3.7"को टैग ऑब्जेक्ट के भीतर <span class="ratingsContent">3.7</span>मौजूद है, जब यह मौजूद है, हालांकि, डिफ़ॉल्ट के लिए NoneTypeजब यह नहीं होता है।

getattr(object, name[, default])

ऑब्जेक्ट की नामित विशेषता का मान लौटाएं। नाम एक तार होना चाहिए। यदि स्ट्रिंग ऑब्जेक्ट की विशेषताओं में से एक का नाम है, तो परिणाम उस विशेषता का मूल्य है। उदाहरण के लिए, getattr (x, 'foobar') x.foobar के बराबर है। यदि नामित विशेषता मौजूद नहीं है, तो डिफ़ॉल्ट प्रदान किए जाने पर वापस कर दिया जाता है, अन्यथा, गुणनफल उठाया जाता है।


0
from bs4 import BeautifulSoup
from bs4.element import Comment
import urllib.request
import re
import ssl

def tag_visible(element):
    if element.parent.name in ['style', 'script', 'head', 'title', 'meta', '[document]']:
        return False
    if isinstance(element, Comment):
        return False
    if re.match(r"[\n]+",str(element)): return False
    return True
def text_from_html(url):
    body = urllib.request.urlopen(url,context=ssl._create_unverified_context()).read()
    soup = BeautifulSoup(body ,"lxml")
    texts = soup.findAll(text=True)
    visible_texts = filter(tag_visible, texts)  
    text = u",".join(t.strip() for t in visible_texts)
    text = text.lstrip().rstrip()
    text = text.split(',')
    clean_text = ''
    for sen in text:
        if sen:
            sen = sen.rstrip().lstrip()
            clean_text += sen+','
    return clean_text
url = 'http://www.nytimes.com/2009/12/21/us/21storm.html'
print(text_from_html(url))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.