53895


先月、質問をさせていただいた者です。質問は「[xpathでデータが未定義の箇所がある場合、どのように例外処理をすれば良いか](https://ja.stackoverflow.com/questions/53390/xpath%e3%81%a7%e3%83%87%e3%83%bc%e3%82%bf%e3%81%8c%e6%9c%aa%e5%ae%9a%e7%be%a9%e3%81%ae%e7%ae%87%e6%89%80%e3%81%8c%e3%81%82%e3%82%8b%e5%a0%b4%e5%90%88-%e3%81%a9%e3%81%ae%e3%82%88%e3%81%86%e3%81%ab%e4%be%8b%e5%a4%96%e5%87%a6%e7%90%86%e3%82%92%e3%81%99%e3%82%8c%e3%81%b0%e8%89%af%e3%81%84%e3%81%8b)」で、これに続く内容です。  
使っている元データは科研費のDBで <https://kaken.nii.ac.jp/>
からアクセスしてXMLデータとしてダウンロードできます。ダウンロードしたXMLから必要なデータだけを抽出して、CSVファイルを作っています。  
当初の目的は達成し、うまく動くようになったのですが、機能を拡張していく中で、別のことも必要になってきました。

例えば <https://kaken.nii.ac.jp/search/?kw=KAKENHI-PROJECT-18K09063>
から、画面上の方にある[XMLで出力]の隣の実行ボタンを押してXMLデータをダウンロードすると、

#

[code]

    <grantAward>
        <summary xml:lang="ja">
            <member sequence="1" eradCode="60196904" role="principal_investigator">～
            <member sequence="2" eradCode="10508526" role="co_investigator_buntan">～
            <member sequence="3" eradCode="20378285" role="co_investigator_buntan">～
    
[/code]

のように、sequenceが1, 2, 3～順に並ぶ、メンバーリストがある構造になっています。ここで、前回の処理と並行して各summaryに、

・sequenceが何番まであるのか探索して数値を出力する  
・sequence=1～n内の情報(ここではeradCode)を連続出力(TAB区切り)する

ことが出来ないか考えています。sequenceの数は定められておらず、可変です。（大半のデータは1ですが、大きいものだと50超のものもあります）

今試しているコードは下記のような状態です（本当はもっと長いですが、短縮してあります）。

[code]

    from lxml import etree      # lxmlライブラリを使用
    tree = etree.parse('ファイル名')
    root = tree.getroot()
    
    awards = tree.xpath('/grantAwards/grantAward')
    
    LstGrantid  = list()
    LstErad     = list()
    LstMembers  = list()
    
    if len(awards):
        for m in awards:
            # 科研費ID
            grantid  = m.xpath('@id')
            eradname = m.xpath('summary[@xml:lang="ja"]/member[@sequence="1"]/@eradCode')
    
            # 取得した要素をリスト変数へ代入
            if len(grantid):        # 科研費ID
                LstGrantid.append(grantid[0])
            else:
                LstGrantid.append("")
    
            if len(eradname):       # 1st名前（Erad）
                LstErad.append(eradname[0])
            else:
                LstErad.append("")
    
            # 共同研究者リストを作る　★うまくいっていない
            memberlst = tree.xpath('/grantAwards/grantAward/summary[@xml:lang="ja"]/member')
            if len(memberlst):
                for n in memberlst:
                    membersu = n.xpath('member[@sequence=n]/@eradCode') # メンバー（Erad）
                    if len(membersu):
                        LstMembers.append(membersu[0])
                    else:
                        LstMembers.append("")
                print(len(LstMembers), end='\t')    # 共同人数
    
    
        # 結果をTAB区切りで標準出力
        for j in range(len(LstGrantid)):
            print(str(LstGrantid[j]), end='\t')     # 科研費ID
            print(str(LstErad[j]), end='\t')        # 1st名前（Erad）
    
    #        print(len(LstMembers), end='\t')   # ★うまくいっていない
            print()
    
[/code]

結果はTAB区切りあるいはカンマ区切りで、人数(11)の後、Eradを羅列するもの：  
KAKENHI-PROJECT-18K09063 60196904 11 10508526 20378285 20422826 20727664
40308664 40444442 50362687 50467362 60647121 80763739

人数(11)の後、Eradと名前を羅列するもの：  
KAKENHI-PROJECT-18K09063 60196904 11 10508526 西村 明展 20378285 淺沼 邦洋 20422826 明田
浩司 20727664 竹上 徳彦 40308664 長谷川 正裕 40444442 辻井 雅也 50362687 若林 弘樹 50467362 中村 知樹
60647121 内藤 陽平 80763739 刀根 慎恵

を考えています。よろしくお願いいたします。

（質問の追記１）  
データが１件ならば動くのですが、複数件あるとエラーが出てしまう  
kaken.nii.ac.jp/search/?o1=1&s1=2018&s2=2018&s3=1
から、画面にある[XMLで出力]隣の実行ボタンを押してXMLデータをダウンロードしたものは  
File "test.py", line 33, in membersu = n.xpath('@eradCode')[0] IndexError:
list index out of range  
になってしまう。

※eradCodeを持っていない研究者の方がいるため

（質問の追記２）  
新し目のデータにも未登録になるケースがある。  
（１）名前だけ羅列するモード（Eradの有無は考慮しない）  
（２）Eradだけ羅列するモード（Erad未登録のものはスキップする）  
（３）Eradと名前を合わせて羅列し、Erad未登録者についてはEradの部分をnullにして、名前だけ出す  
といったアプローチが考えられる。また、の方にしか personalName/fullName がないケースがあるようだ。

※名前がなけれは出力なし、として対応した


CC BY-SA 4.0
2019-04-01T11:33:31.737
53853
2019-04-12T04:31:45.813
2019-04-12T04:31:45.813
32510
32510
question
python python3 xpath
xpathで要素数が可変のデータを抽出する方法


もうちょっとのところまで実装できてそうです。  
`for n in memberlst:`のループの中の`n`がどんな状態か、というところがポイントですね。

`n`の時点で`member`タグが一番上位になっているので、xpathで要素を取得するときは、子要素なり属性なりを指定するだけでよいはずです(memberタグの指定はいらない)。

ついでに、First Authorだけをxpathで取得する必要もないかもしれません。

First Author以外の著者のEradと名前を羅列するなら、以下のような感じだと思います。  
名前が不要なら`name`関連の処理を消してください。

[code]

    from lxml import etree      # lxmlライブラリを使用
    tree = etree.parse('./kaken.nii.ac.jp_2019-04-12_00-34-28.xml')
    root = tree.getroot()
    
    awards = tree.xpath('/grantAwards/grantAward')
    
    records =list()
    
    if len(awards):
        for award in awards:
    
            record = list()
    
            # 科研費ID
            grant_id  = award.attrib.get('id') or ''
            record.append(grant_id)
    
            # 研究者リストを作る
            members = award.xpath('summary[@xml:lang="ja"]/member')
            for m in members:
    
                # eradCodeがない場合は空文字で補間
                eradCode = m.attrib.get('eradCode') or ''                  
                seq =  m.attrib.get('sequence') or ''          
    
                try:
                    name = m.xpath('personalName/fullName').pop().text
                except IndexError:
                    name = ''
    
                if seq == "1": # first author の処理
                    record.append(eradCode)
                    record.append(str(len(members)))
                else:
                    record.append(eradCode)
                    record.append(name)
    
            records.append(record)
    
    #結果をTAB区切りで標準出力
    for r in records:
        print('\t'.join(r))   
    
[/code]


CC BY-SA 4.0
2019-04-03T14:39:55.950
53895
2019-04-11T15:42:52.877
2019-04-11T15:42:52.877
12561
12561
53853
answer