9

lxmlのetreeを使用して(ローカルに保存された)テキストファイルを解析したいと思います。しかし、私のすべてのファイル(数千)には、次のようなヘッダーがあります。

-----BEGIN PRIVACY-ENHANCED MESSAGE-----
Proc-Type: 2001,MIC-CLEAR
Originator-Name: webmaster@www.sec.gov
Originator-Key-Asymmetric:
 MFgwCgYEVQgBAQICAf8DSgAwRwJAW2sNKK9AVtBzYZmr6aGjlWyK3XmZv3dTINen
 TWSM7vrzLADbmYQaionwg5sDW3P6oaM5D3tdezXMm7z1T+B+twIDAQAB
MIC-Info: RSA-MD5,RSA,
 AHxm/u6lqdt8X6gebNqy9afC2kLXg+GVIOlG/Vrrw/dTCPGwM15+hT6AZMfDSvFZ
 YVPEaPjyiqB4rV/GS2lj6A==

<SEC-DOCUMENT>0001193125-07-200376.txt : 20070913
<SEC-HEADER>0001193125-07-200376.hdr.sgml : 20070913
<ACCEPTANCE-DATETIME>20070913115715
ACCESSION NUMBER:       0001193125-07-200376
CONFORMED SUBMISSION TYPE:  10-K
PUBLIC DOCUMENT COUNT:      7
CONFORMED PERIOD OF REPORT: 20070630
FILED AS OF DATE:       20070913
DATE AS OF CHANGE:      20070913

そして最初<はこの場合51行目までではありません(そしてすべての場合で51ではありません)。xml部分は次のように始まります。

</SEC-HEADER>
<DOCUMENT>
<TYPE>10-K
<SEQUENCE>1
<FILENAME>d10k.htm
<DESCRIPTION>FORM 10-K
<TEXT>
<HTML><HEAD>
<TITLE>Form 10-K</TITLE>
</HEAD>
 <BODY BGCOLOR="WHITE">
<h5 align="left"><a href="#toc">Table of Contents</a></h5>

これをlxmlでオンザフライで処理できますか?または、ストリームエディタを使用して各ファイルのヘッダーを省略する必要がありますか?ありがとう!

これが私の現在のコードとエラーです。

from lxml import etree
f = etree.parse('temp.txt')

XMLSyntaxError: Start tag expected, '<' not found, line 1, column 1

編集:

FWIW、ここにファイルへのリンクがあります。

4

4 に答える 4

6

これらのファイルには標準があるため、物事を推測したり、beautifulsoup が物事を正しく処理することを期待したりするのではなく、適切なパーサーを作成することができます。それがあなたにとって最良の答えであることを意味するわけではありませんが、それは確かに見ている仕事です.

http://www.sec.gov/info/edgar/pdsdissemspec910.pdfの標準によると、(PEM エンクロージャ内に) あるものは、提供された DTD によって定義された SGML ドキュメントです。まず、48 ~ 55 ページに移動して、そこにあるテキストを抽出し、たとえば「edgar.dtd」として保存します。

私が最初に行うことは、 SPをインストールし、そのツールを使用して、ドキュメントが本当に有効であり、その DTD によって解析可能であることを確認して、うまくいかないことに多くの時間を無駄にしないようにすることです。アウト。

Python には、検証用の SGML パーサーである sgmllib が付属しています。残念ながら、完全に完成することはなく、2.6-2.7 で非推奨になりました (そして 3.x で削除されました)。しかし、それはうまくいかないという意味ではありません。それで、試してみて、それが機能するかどうかを確認してください。

そうでない場合、私は Python で適切な代替手段を知りません。世の中に出回っている SGML コードのほとんどは、C、C++、または Perl で書かれています。ただし、C/Cython/boost-python/whatever または ctypes を使用してラップされた独自のコードを快適に作成できる限り、任意の C または C++ ライブラリ (私は SP から始めます) を非常に簡単にラップできます。バインディングの完全なセットを構築する必要はなく、最上位の関数をラップするだけで済みます。しかし、これまでにこのようなことをしたことがない場合は、おそらく学ぶのに最適な時期ではありません.

または、コマンドライン ツールをまとめることもできます。SP には nsgmls が付属しています。同じ名前の perl で書かれた別の優れたツールがあります ( http://savannah.nongnu.org/projects/perlsgml/の一部だと思いますが、確信は持てません)。その他にも多数のツールがあります。

または、もちろん、Python の代わりに perl (または C++) で全体または解析層だけを作成することもできます。

于 2012-09-14T19:51:30.507 に答える
4

PEM ( RFC 1421で指定されているプラ​​イバシー拡張メッセージ)のカプセル化されたテキストに簡単にアクセスできます。これには、カプセル化の境界を取り除き、その間のすべてを最初の空白行でヘッダーとカプセル化されたテキストに分離します。

SGML の構文解析は、はるかに困難です。EDGARのドキュメントで動作するように見える試みを次に示します。

from lxml import html

PRE_EB = "-----BEGIN PRIVACY-ENHANCED MESSAGE-----"
POST_EB = "-----END PRIVACY-ENHANCED MESSAGE-----"

def unpack_pem(pem_string):
    """Takes a PEM encapsulated message and returns a tuple
    consisting of the header and encapsulated text.  
    """

    if not pem_string.startswith(PRE_EB):
        raise ValueError("Invalid PEM encoding; must start with %s"
                         % PRE_EB)
    if not pem_string.strip().endswith(POST_EB):
        raise ValueError("Invalid PEM encoding; must end with %s"
                         % POST_EB)
    msg = pem_string.strip()[len(PRE_EB):-len(POST_EB)]
    header, encapsulated_text = msg.split('\n\n', 1)
    return (header, encapsulated_text)


filename = 'secdoc_htm.txt'
data = open(filename, 'r').read()

header, encapsulated_text = unpack_pem(data)

# Now parse the SGML
root = html.fromstring(encapsulated_text)
document = root.xpath('//document')[0]

metadata = {}
metadata['type'] = document.xpath('//type')[0].text.strip()
metadata['sequence'] = document.xpath('//sequence')[0].text.strip()
metadata['filename'] = document.xpath('//filename')[0].text.strip()

inner_html = document.xpath('//text')[0]

print(metadata)
print(inner_html)

結果:

{'filename': 'd371464d10q.htm', 'type': '10-Q', 'sequence': '1'}

<Element text at 80d250c>
于 2012-09-13T21:19:40.730 に答える
1

これにはBeautifulSoupを使用できます。

>>> from BeautifulSoup import BeautifulStoneSoup
>>> soup = BeautifulStoneSoup(xmldata)
>>> print soup.prettify()
-----BEGIN PRIVACY-ENHANCED MESSAGE-----
Proc-Type: 2001,MIC-CLEAR
Originator-Name: webmaster@www.sec.gov
Originator-Key-Asymmetric:
 MFgwCgYEVQgBAQICAf8DSgAwRwJAW2sNKK9AVtBzYZmr6aGjlWyK3XmZv3dTINen
 TWSM7vrzLADbmYQaionwg5sDW3P6oaM5D3tdezXMm7z1T+B+twIDAQAB
MIC-Info: RSA-MD5,RSA,
 AHxm/u6lqdt8X6gebNqy9afC2kLXg+GVIOlG/Vrrw/dTCPGwM15+hT6AZMfDSvFZ
 YVPEaPjyiqB4rV/GS2lj6A==
<sec-document>
 0001193125-07-200376.txt : 20070913
 <sec-header>
  0001193125-07-200376.hdr.sgml : 20070913
  <acceptance-datetime>
   20070913115715
ACCESSION NUMBER:       0001193125-07-200376
CONFORMED SUBMISSION TYPE:  10-K
PUBLIC DOCUMENT COUNT:      7
CONFORMED PERIOD OF REPORT: 20070630
FILED AS OF DATE:       20070913
DATE AS OF CHANGE:      20070913
  </acceptance-datetime>
 </sec-header>
 <document>
  <type>
   10-K
   <sequence>
    1
    <filename>
     d10k.htm
     <description>
      FORM 10-K
      <text>
       <html>
        <head>
         <title>
          Form 10-K
         </title>
        </head>
        <body bgcolor="WHITE">
         <h5 align="left">
          <a href="#toc">
           Table of Contents
          </a>
         </h5>
        </body>
       </html>
      </text>
     </description>
    </filename>
   </sequence>
  </type>
 </document>
</sec-document>
于 2012-09-13T20:55:08.123 に答える
1

問題の定義は、最初の '<' から解析を開始することを暗示していますが、これは良い考えではないと思います。これらは PEM ヘッダーのように見え (そうでない場合は、RFC(2)822 から派生したものです)、「<」文字が含まれている可能性があります。たとえば、Originator-Name: "Foo Bar" <foo@bar.edu>ある日を見つけることができます。あなたが見ている特定のファイルは決してそうではない可能性がありますが、それを確実に知ることができない限り、それに依存しない方が良いでしょう.

これを XML 本文を含む RFC822 メッセージとして実際に解析したい場合、それは非常に簡単です。

with file('temp.txt') as f:
  rfc822.Message(f).rewindbody()
  x = etree.parse(f)

しかし、技術的には、これは PEM には有効ではありません (PEM のヘッダーボディ形式は、参照によって組み込まれるのではなく、実質的に RFC822 のフォークであるため)。また、他のさまざまな同様のRFC822形式では実際には有効ではない場合もあります。そして実際に気にすることは、ヘッダーとボディがどのように分離されるかということだけです。これは非常に単純なルールです。

with file('temp.txt') as f:
  while f.readline():
    pass
  x = etree.parse(f)

もう 1 つの選択肢は、本体が常に SEC-DOCUMENT ノードであるという (明らかな) 事実に依存することです。

with file('temp.txt') as f:
  text = f.read()
body = '<SEC-DOCUMENT>' + text.split('<SEC-DOCUMENT>, 1)[1]
x = etree.fromstring(body)

最後の注意: 一般に、RFC822 ヘッダーが表示されると、その形式が実際に完全な RFC2822 + オプションの MIME であるかどうかという疑問が生じます。コンテンツ ヘッダーがどこにもないという事実は、ここではおそらく安全であることを意味しますが、それらの大量のコレクションを grep することをお勧めします (または、ファイル形式の定義がどこかにある場合は、それをざっと読みます)。

于 2012-09-13T20:48:34.877 に答える