質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

406閲覧

ローカルのテキストファイル(約3GB)の処理を早くしたい〜!

hacosato

総合スコア48

Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/02/10 13:51

前提・実現したいこと

ローカルにWikipediaのダンプを保存して、それの処理をしています。時間がかかるので早くやりたいです!

http://taka-say.hateblo.jp/entry/2016/05/20/221817
こちらのブログの内容の通り、Wikipediaのデータを落としてきてローカルに保存しました。
ファイルは約500KBごとに分割されていて、全部で約2.96GBあります。
中身はブログに書いてある通り、<doc>というタグでくくられたxmlみたいなファイルです。

その中から、BeautifulSoupを使って、指定したキーワードの記事を抽出するPythonを書きました。
以下、動作します!

Python

1import os 2from bs4 import BeautifulSoup 3import time 4 5def files(path): 6 for pathname, dirnames, filenames in os.walk(path): 7 for filename in filenames: 8 yield os.path.join(pathname, filename) 9 10starttime = time.time() 11cd = 'ぱす〜〜/Wikipedia_dump190121/extracted/' 12q = '恋するフォーチュンクッキー' 13for path in files(cd): 14 if 'wiki_' in path: 15 with open(path) as f: 16 soup = BeautifulSoup(f, 'html.parser') 17 article = soup.find_all('doc', title=q) 18 if article: 19 print(article) 20endtime = time.time() 21print(endtime-starttime, ' sec')

しかしめちゃくちゃ遅くて、さっきやったところ105秒もかかりました。
1秒ぐらいでできたらうれしいなと思っているんですが、方法はありますでしょうか?

試したこと

ファイルの開閉に時間がかかるからかな?と思い、上記ブログの通りぜんぶファイルをつなげました。

find extracted -name 'wiki*' -exec cat {} \; > text.xml

xmlファイルひとつにまとまりました。約2.81GBあります。
上記Pythonと同じような感じで処理させてみたところ、下記のようにエラーになりました。重すぎるからかも…。

OSError Traceback (most recent call last) <ipython-input-53-b974053833d4> in <module>() 4 q = '恋するフォーチュンクッキー' 5 with open('ぱす〜〜/Wikipedia_dump190121/jawiki-latest-pages-articles.xml.bz2') as f: ----> 6 soup = BeautifulSoup(f, 'html.parser') 7 article = soup.find_all('doc', title=q) 8 if article: /usr/local/lib/python3.6/site-packages/bs4/__init__.py in __init__(self, markup, features, builder, parse_only, from_encoding, exclude_encodings, **kwargs) 189 190 if hasattr(markup, 'read'): # It's a file-type object. --> 191 markup = markup.read() 192 elif len(markup) <= 256 and ( 193 (isinstance(markup, bytes) and not b'<' in markup) OSError: [Errno 22] Invalid argument

補足情報(FW/ツールのバージョンなど)

Python 3.6.5
jupyter 4.4.0
ストレージはSSDです。
ほかに必要な情報あれば教えてください…。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tiitoi

2019/02/10 13:59

各ファイルの処理は独立して行えるので、1ファイル分処理する関数作って、マルチスレッドで並列化して動かせば、高速化できるかと思います。 コード見る限り、それぐらいしか高速化する余地がありません。
hacosato

2019/02/10 14:29

ありがとうございます! やってみます!
hacosato

2019/02/11 03:01

やってみました! hayataka2049さんの書いてくださった分があるので、書くこと自体はすぐできました! https://qiita.com/castaneai/items/9cc33817419896667f34 こちら参考にスレッドプールを使いました。 その結果、クエリ1つで106秒、10コで720秒でした。思ったほど早くない……。
hacosato

2019/02/11 03:03

いや、ごめんなさいこれ違う…書き直します…。
hacosato

2019/02/11 04:23

やってみたところ、クエリ1つで111秒、10コで700秒ちょうどでした…。 あんまり上手に生かせないです……。
guest

回答2

0

ベストアンサー

処理内容的に1秒はなかなかきついでしょうねぇ。

とりあえず安直にプロセス並列化。これで数倍くらいにはなりませんか。

python

1import os 2import time 3from multiprocessing import Pool 4 5from bs4 import BeautifulSoup 6 7def files(path): 8 for pathname, dirnames, filenames in os.walk(path): 9 for filename in filenames: 10 yield os.path.join(pathname, filename) 11 12def process(args): 13 path, q = args 14 with open(path) as f: 15 soup = BeautifulSoup(f, "html.parser") 16 article = soup.find_all("doc", title=q) 17 return article 18 19if __name__ == "__main__": 20 p = Pool(コア数の倍くらいの整数) 21 cd = "ぱす〜〜/Wikipedia_dump190121/extracted/" 22 targets = [path for path in files(cd) if "wiki_" in path] 23 q = "恋するフォーチュンクッキー" 24 result = [x for x in p.map(process, ((t, q) for t in targets)) if x] 25 print(result)

投稿2019/02/10 14:29

編集2019/02/10 15:21
hayataka2049

総合スコア30933

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hacosato

2019/02/10 15:00 編集

ありがとうございました! ローカルのデータなのに1秒はむずかしいのか…ネットってすごい…。 ご提示いただいたのを実行すると、こういうエラーが起きました! def files(args):のargsが使われてないみたいに思うんですが、 どこか取り違えでしょうか…? --------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-62-9208cabdb828> in <module>() 20 p = Pool(8) 21 cd = ぱす〜〜/Wikipedia_dump190121/extracted/' ---> 22 targets = [path for path in files(cd) if "wiki_" in path] 23 q = "恋するフォーチュンクッキー" 24 result = [x for x in p.map(process, ((t, q) for x in targets)) if x] <ipython-input-62-9208cabdb828> in <listcomp>(.0) 20 p = Pool(8) 21 cd = 'ぱす〜〜/Wikipedia_dump190121/extracted/' ---> 22 targets = [path for path in files(cd) if "wiki_" in path] 23 q = "恋するフォーチュンクッキー" 24 result = [x for x in p.map(process, ((t, q) for x in targets)) if x] <ipython-input-62-9208cabdb828> in files(args) 6 7 def files(args): ----> 8 path, q = args 9 for pathname, dirnames, filenames in os.walk(path): 10 for filename in filenames: ValueError: too many values to unpack (expected 2)
hayataka2049

2019/02/10 14:57

ごめんなさい、素でミスりました。修正しておきます。
hacosato

2019/02/10 15:19

ありがとうございます! 最後から2番目のこの行は、 result = [x for x in p.map(process, ((t, q) for x in targets)) if x] (t, q)のところ(x, q)が正しいと思いましたのでそれで直しました。 さらに、時間を計測する部分を付け加えて動かしたところ、 Jupyter notebookが下記のようなよくわかんないエラーを出すようになりました…。 ERROR:root:Internal Python error in the inspect module. Below is the traceback from this internal error. そこで、ローカルの.pyファイルに書いて、ターミナルで動かしました。 すると今度は下記のような結果が出ました。 中略のところに求めている記事の内容が出るのですが、 時間が表示されないので、処理が途中で止まってしまっているみたいに思いました。 --------------------------------------------------------------------------- Traceback (most recent call last): File "ぱす〜〜/Desktop/untitled.py", line 25, in <module> result = [x for x in p.map(process, ((x, q) for x in targets)) if x] File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 266, in map return self._map_async(func, iterable, mapstar, chunksize).get() File "/usr/local/Cellar/python/3.6.5/Frameworks/Python.framework/Versions/3.6/lib/python3.6/multiprocessing/pool.py", line 644, in get raise self._value multiprocessing.pool.MaybeEncodingError: Error sending result: '[[], [], … [], [<doc 中略…</doc>], [], … []]'. Reason: 'RecursionError('maximum recursion depth exceeded while calling a Python object',)'
hayataka2049

2019/02/10 15:25

xに関しては間違いです。下のエラーはなんだろう、これ。 import sys sys.setrecursionlimit(10**6) とかをimportのあたりに書いてみてください。
hacosato

2019/02/10 15:34

うまくいきました! ありがとうございます〜! 所要時間は105秒だったのが15秒になりました! 1/7になったぞ!! 教えていただいたのはマルチプロセスという方法と理解しています。 あしたマルチスレッドも試してみます〜!
hayataka2049

2019/02/10 15:40 編集

実行中のCPU利用率が100%より低くて、メモリ容量に余裕があるうちは並列化するプロセス数(コア数の倍くらいの整数のところの数字)を増やせます。並列に走る数を増やせば、あるプロセスではCPUで処理している間に他プロセスがストレージを読むというふうに高速化できます。 最終的にストレージ律速なので、SSDの読み出し速度の限界に達するまでは高速化できるはずです。300MB/sとして10秒くらい。そう考えると割とすでに限界に近いですが、もう少し増やしてみる余地はあるかもしれません。
hacosato

2019/02/10 16:17

数値を増やしたら12秒ぐらいまでいきました〜! Pool(16)です。そこから先は、数値を増やしてもほとんど時間変わらなかったです…。 いまqのところは決め打ちですけど、このクエリが1000個とかあった場合、各クエリもプロセスにすることはできるのでしょうか? クエリを2階建てにしても弊害はないのでしょうか?(自分でやってみればいいよね…と自分にツッコミ)
hayataka2049

2019/02/10 16:30 編集

2階建てはともかく(原理的にはできるけど面倒くさい)、クエリが変わるごとに同じファイルをいちいちopenしても無意味ですからねぇ。 process関数の書き方を工夫して複数クエリ同時に受け取れば良いでしょう。 たとえばqをクエリのリストにして、process関数はそのつもりで受け取って然るべく処理するとか。
hacosato

2019/02/10 17:00

そうでした…。 ファイルの開け閉めに時間がかかっているっぽいので、そのほうがよさそうですね! やってみます!!
hacosato

2019/02/11 02:58

やってみました! qを10コのリストにして回してみたところ、94秒でした! もしファイルの開け閉め以外の操作に時間がまったくかからなかったら、12秒のままで10コのクエリを処理できるはず〜(1つあたり1.2秒!)と思ったのに意外とそんなことはなかったです…。 いまの技量でできる範囲だとこれが限界かな…。
guest

0

メモリをマウントしてそこにファイルを保存すれば読み込み速度は爆速になるかと思います。

投稿2019/02/10 14:35

scsi

総合スコア2840

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hacosato

2019/02/10 15:00

ありがとうございます。 「メモリをマウント」というのは、xmlファイルをまとめてRAMに載せる、という感じでしょうか? なんか難しそうな感じがするのですが、検索するためのキーワードよければ教えていただけますか? (「メモリをマウント Python」だけだとRaspberryの話がたくさん出てくる…)
hacosato

2019/02/11 02:04

ありがとうございます! Python関係ないのですね……。 「RAMディスク Mac」でいろいろ見つけられた気がします!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問