2016-05-17 3 views
2

Ich arbeite an einem Projekt, um spezifische Informationen aus lokal gespeicherten HTML-Dateien mit BS4 zu extrahieren. Da ich sehr viele Dateien habe (> 1 Million), ist Geschwindigkeit und Leistung der Schlüssel für einen nützlichen Code, der alle Dateien durchsucht. Bis jetzt arbeite ich mit BS4, da ich vorher an einem Web-Crawler gearbeitet habe und ich dachte BS4 ist ziemlich einfach und praktisch. Wenn es jedoch um Big Data geht, ist BS4 viel zu langsam. Ich lese über die lxml parser und html.parser, die die einfachsten und schnellsten Parser unter Python für HTML-Dokumente zu sein scheint.Codetransformation von BS4 nach lxml-Parser

So jetzt mein Code wie folgt aussieht:

from bs4 import BeautifulSoup 
import glob 
import os 
import re 
import contextlib 


@contextlib.contextmanager 
def stdout2file(fname): 
    import sys 
    f = open(fname, 'w') 
    sys.stdout = f 
    yield 
    sys.stdout = sys.__stdout__ 
    f.close() 


def trade_spider(): 
    os.chdir(r"C:\Users\XXX") 
    with stdout2file("output.txt"): 
     for file in glob.iglob('**/*.html', recursive=True): 
      with open(file, encoding="utf8") as f: 
       contents = f.read() 
       soup = BeautifulSoup(contents, "html.parser") 
       for item in soup.findAll("ix:nonfraction"): 
        if re.match(".*SearchTag", item['name']): 
         print(file.split(os.path.sep)[-1], end="| ") 
         print(item['name'], end="| ") 
         print(item.get_text()) 
         break 
trade_spider() 

Es eine Textdatei öffnet, geht in mein Set-Verzeichnis (os.chdir (..)), alle Dateien seraches durch .html endet, liest die Inhalt und wenn es ein Tag mit dem Namensattribut "SearchTag" findet, nimmt es den zugehörigen HTML-Text und druckt es in meine geöffnete Textdatei. Nach einem Match gibt es eine Pause und es wird mit der nächsten fortgesetzt. Also was ich gelesen habe ist, dass BS4 dies alles im Speicher macht, was die Verarbeitungszeit signifikant erhöht.

Deshalb wollte ich meinen Code ändern, entweder mit lxml (bevorzugt) oder html.parser.

Jeder von euch ist ein Genie und ist in der Lage, meinen Code zu ändern, um lxml Parser zu verwenden, ohne die anfängliche einfache Idee, die ich dabei hatte, zu ändern?

Jede auf diese geschätzte Hilfe, da ich total stucked bin ....

UPDATE:

import lxml.etree as et 
import os 
import glob 

import contextlib 


@contextlib.contextmanager 
def stdout2file(fname): 
    import sys 
    f = open(fname, 'w') 
    sys.stdout = f 
    yield 
    sys.stdout = sys.__stdout__ 
    f.close() 


def skip_to(fle, line): 
     with open(fle) as f: 
      pos = 0 
      cur_line = f.readline().strip() 
      while not cur_line.startswith(line): 
       pos = f.tell() 
       cur_line = f.readline() 
      f.seek(pos) 
      return et.parse(f) 


def trade_spider(): 
    os.chdir(r"F:\04_Independent Auditors Report") 
    with stdout2file("auditfeesexpenses.txt"): 
     for file in glob.iglob('**/*.html', recursive=True): 
      xml = skip_to(file, "<?xml") 
      tree = xml.getroot() 
      nsmap = {"ix": tree.nsmap["ix"]} 
      fractions = xml.xpath("//ix:nonFraction[contains(@name, 'AuditFeesExpenses')]", namespaces=nsmap) 
      for fraction in fractions: 
       print(file.split(os.path.sep)[-1], end="| ") 
       print(fraction.get("name"), end="| ") 
       print(fraction.text, end=" \n") 
       break 

trade_spider() 

ich diese Fehlermeldung:

Traceback (most recent call last): 
    File "C:/Users/6930p/PycharmProjects/untitled/Versuch/lxmlparser.py", line 43, in <module> 
    trade_spider() 
    File "C:/Users/6930p/PycharmProjects/untitled/Versuch/lxmlparser.py", line 33, in trade_spider 
    xml = skip_to(file, "<?xml") 
    File "C:/Users/6930p/PycharmProjects/untitled/Versuch/lxmlparser.py", line 26, in skip_to 
    return et.parse(f) 
    File "lxml.etree.pyx", line 3427, in lxml.etree.parse (src\lxml\lxml.etree.c:79720) 
    File "parser.pxi", line 1803, in lxml.etree._parseDocument (src\lxml\lxml.etree.c:116182) 
    File "parser.pxi", line 1823, in lxml.etree._parseFilelikeDocument (src\lxml\lxml.etree.c:116474) 
    File "parser.pxi", line 1718, in lxml.etree._parseDocFromFilelike (src\lxml\lxml.etree.c:115235) 
    File "parser.pxi", line 1139, in lxml.etree._BaseParser._parseDocFromFilelike (src\lxml\lxml.etree.c:110109) 
    File "parser.pxi", line 573, in lxml.etree._ParserContext._handleParseResultDoc (src\lxml\lxml.etree.c:103323) 
    File "parser.pxi", line 679, in lxml.etree._handleParseResult (src\lxml\lxml.etree.c:104936) 
    File "lxml.etree.pyx", line 324, in lxml.etree._ExceptionContext._raise_if_stored (src\lxml\lxml.etree.c:10656) 
    File "parser.pxi", line 362, in lxml.etree._FileReaderContext.copyToBuffer (src\lxml\lxml.etree.c:100828) 
    File "C:\Users\6930p\Anaconda3\lib\encodings\cp1252.py", line 23, in decode 
    return codecs.charmap_decode(input,self.errors,decoding_table)[0] 
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 1789: character maps to <undefined> 
+1

Mit lxml selbst den schnellsten Ansatz sein, fügen Sie eine Probe von Ihre Daten –

+0

@PadraicCunningham: Bearbeitete OP mit einer Probe. Dies ist jedoch nur ein sehr kurzer Abschnitt, die ursprünglichen HTML-Dateien enthalten bis zu 3000 Zeilen .... –

+0

Was möchten Sie analysieren?Gibt es auch Namespaces? –

Antwort

3

Es ein bisschen Arbeit ist um den HTML-Code gemäß Ihrer HTML-Datei in pastebin aufzuräumen, findet der folgende Code nonFraction Tags mit Namensattributen, die 'AuditFeesExpenses' enthalten:

import lxml.etree as et 

def skip_to(fle, line): 
    with open(fle) as f: 
     pos = 0 
     cur_line = f.readline().strip() 
     while not cur_line.startswith(line): 
      pos = f.tell() 
      cur_line = f.readline() 
     f.seek(pos) 
     return et.parse(f) 

xml = skip_to("/home/padraic/Downloads/sample_html_file.html","<?xml") 
tree = xml.getroot() 
# one mapping is None -> None: 'http://www.w3.org/1999/xhtml' 
nsmap = {k: v for k, v in tree.nsmap.items() if k} 

print(xml.xpath("//ix:nonFraction[contains(@name, 'AuditFeesExpenses')]", namespaces=nsmap)) 

Ausgang:

[<Element {http://www.xbrl.org/2008/inlineXBRL}nonFraction at 0x7f5b9e91c560>, <Element {http://www.xbrl.org/2008/inlineXBRL}nonFraction at 0x7f5b9e91c5a8>] 

den Text und den Namen zu ziehen:

fractions = xml.xpath("//ix:nonFraction[contains(@name, 'AuditFeesExpenses')]", namespaces=nsmap) 

for fraction in fractions: 
    print(fraction.get("name")) 
    print(fraction.text) 

die Ihnen:

ns19:AuditFeesExpenses 
1,850 
ns19:AuditFeesExpenses 
2,400 

Auch die, wenn Sie nur mit ix Namespace können Sie nur das

ziehen
tree = xml.getroot() 
nsmap = {"ix":tree.nsmap["ix"]} 

fractions = xml.xpath("//ix:nonFraction[contains(@name, 'AuditFeesExpenses')]", namespaces=nsmap) 

for fraction in fractions: 
    print(fraction.get("name")) 
    print(fraction.text) 

So ist der volle woking Code:

def trade_spider(): 
    os.chdir(r"C:\Users\Independent Auditors Report") 
    with stdout2file("auditfeesexpenses.txt"): 
     for file in glob.iglob('**/*.html', recursive=True): 
      xml = skip_to(file, "<?xml") 
      tree = xml.getroot() 
      nsmap = {"ix": tree.nsmap["ix"]} 
      fractions = xml.xpath("//ix:nonFraction[contains(@name, 'AuditFeesExpenses')]", namespaces=nsmap) 
      for fraction in fractions: 
       print(file.split(os.path.sep)[-1], end="| ") 
       print(fraction.get("name"), end="| ") 
       print(fraction.text, end="|") 

Anstelle von os.chdir können Sie auch:

for file in glob.iglob('C:/Users/Independent Auditors Report/**/*.html', recursive=True): 
+0

Perfekt! Schätzen Sie Ihre Hilfe dabei! Ich werde diesen Code für ein paar Beispiele auf meinem Computer ausführen und Ihnen Feedback geben. Nur eine Frage, wie bekomme ich den Operator os.chdir in diesen Code, da ich einen rohen Verzeichnispfad in meinen Code einfügen muss, um ein ganzes Verzeichnis und seinen Unterordner zu durchsuchen? –

+0

Behalten Sie Ihre eigene Logik, übergeben Sie einfach Datei an die Funktion skip_to, es gibt einige andere Optionen mit lxml, aber sehen Sie, wie Sie mit der Logik oben gehen, und wir können von dort gehen –

+0

OP bearbeiten. Wenn ich Ihre Version des Codes verwende, der nur eine Datei mit dem Operator "xml = skip_to (...)" analysiert, funktioniert das perfekt. Aber wenn ich versuche, meine Logik von os.chdir einzufügen, bekomme ich den Code nicht funktionierend? –

Verwandte Themen