paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

スクレイピングでfワードを数える2

最近映画視聴記録がどうのという記事を書いていたのですが、映画関連で昔パルプフィクション絡みのやつを書いたことを思い出しました。
内容はタイトルの通りなのですが、以前の記事ではやり残しがありました。
プログラム歴も増えてきたので今なら出来るっしょということでリメイク版です。

前回のおさらい

paloma69.hatenablog.com

  • 台本上のfワードは169個
  • やり残し
    1. セリフの成形
    2. 件数の表示
    3. 誰が何回言ったかのサマリ

コード

今回は全処理関数化してライブラリ的に呼び出せる様にしました。
(わざわざ呼び出して使う機会は無いと思うが)
引数の値もう少し統一感出せばよかったな。
今回も一応全編通して伏字にします。

from bs4 import BeautifulSoup
import re
import collections

def create_quote_list():
    with open('Pulp-fiction.html', 'r', encoding='utf-8') as f:
        soup = BeautifulSoup(f.read(), 'html.parser')

    lines = []
    for x in soup.findAll('b'):
        lines.append((x.text, x.next_sibling))

    return lines

def into_list(l, chara, quote):
    shaping_charactor = chara.replace("\n", "").replace(" ", "").replace("(O.S.)", "")
    if shaping_charactor == 'CLOSEUP・MIA':
       shaping_charactor = 'MIA'
    l.append([shaping_charactor, quote])

def scraping_words(script_list):
    s_reg = re.compile('.+[fF]xxk.+')
    flist = []
    for l in script_list:
        split_sentense = re.split('[,.!?]|and', str(l[1]))
        for x in split_sentense:
            w = re.search(s_reg, x)
            if bool(w) is True:
                s = w.group().replace("\n", " ").replace("  ", "")
                if 'AND' in l[0]:
                    for y in l[0].split('AND'):
                        into_list(flist, y, s)
                else:
                    into_list(flist, l[0], s)

    return flist

def fcounter(l):
    max_c, max_w = shapes(l)
    clist = []
    for x in l:
        clist.append(x[0])
    c = collections.Counter(clist).most_common()
    for x in c:
        c, w = x
        print(c.ljust(max_c), w)

def shapes(l):
    max_c, max_w = 0,0
    for f in l:
        if len(f[0]) > max_c:
            max_c = len(f[0])
        if len(f[1]) > max_w:
            max_w = len(f[1])
    return max_c, max_w

def fquote(l):
    max_c, max_w = shapes(l)
    n = 1
    for x in l:
        print(n, x[0].ljust(max_c), x[1].ljust(max_w))
        n +=1

def main():
    lines = create_quote_list()

    flist = scraping_words(lines)
    print('*' * 10, 'print f word list', '*' * 10)
    fquote(flist)
    print('*' * 10, 'print f word count', '*' * 10)
    fcounter(flist)

if __name__ == '__main__':
    main()

スクリプト叩いて終了では味気無いのでインタラクティブシェルでインポートして少し見ましょう。 本当はこの呼び出し方は非推奨です。

>>> from pulp import *

スクレイピング対象

前回と同じくimdbのサイトにscriptがあるので、これをダウンロードしてつかいます。

https://imsdb.com/scripts/Pulp-Fiction.html

スクレイピングのライブラリはBeautifulSoupです。

スクレイピング処理も呼び出して格納。

>>> lines = create_quote_list()

HTML構成

上記サイトのscriptファイルはしっかりと階層化はされてなくて、 bタグでシチュエーションや登場人物、次の階層(タグなし)に状況やセリフが書かれています。 タグが無くてもnext_siblingというmethodで取得できます。 create_quote_list()内でbタグとその次のセリフを取得してます。

    for x in soup.findAll('b'):
        lines.append((x.text, x.next_sibling))

取得した値はこう。

>>> lines[0]
('                                      "PULP FICTION"\n', '\n                                            By\n\n
 Quentin Tarantino & Roger Avary\n\n')
>>> lines[10]
('                                     YOUNG WOMAN\n', '
           You sound like a duck.\n                              (imitates a duck)\n                         Quack, quack, quack, quack, quack, \n                         quack, quack...\n\n')
>>> lines[20]
('                                     YOUNG WOMAN\n', '
           Did they hurt the little girl?\n\n')

スペース、改行記号の処理は別関数のshapingで行います。

ワードの取得

正規表現でワードと前後の文を抽出します。
4文字だけだと同じ文字が続くだけなので、結構バリエーションが出てくるのでせっかくだし前後も取ることにしました。
/w+等使って文章ごとに取りたかったのですが特殊文字や余剰スペースの処理で上手く全件ヒットとはならなかったのでフレーズごとに取得してます。

>>> f = scraping_words(lines)
>>> f[0]
['YOUNGMAN', ' I sound like a sensible fxxking man']
>>> f[10]
['YOUNGMAN', ' fxxk it']
>>> f[100]
['JULES', " Don't you fxxkin' do "]
>>> f[20]
['VINCENT', " but you're in the same fxxkin' "]

少し短いですが誰がどんな感じのこと言ってるかの雰囲気はわかります。

カウントのためリストに格納

上記と同じくscraping_words()の処理の話ですがカウントできるよう取得項目を人物、セリフに分割してリストにいれていきます。
文章を正規表現で抜き出すというのはやはりブレが出るため難しく、フレーズごとに分割してから対象のワードをひっかける方法にしました。
(件数が合わなくてあれ?となり何回か方法を施行しました)

一件だけ2人が同時に叫ぶ所がありますが、人物をカウントしたいのでここもさらに分割してます。

def scraping_words(script_list):
    s_reg = re.compile('.+[fF]xxk.+')
    flist = []
    for l in script_list:
        split_sentense = re.split('[,.!?]|and', str(l[1]))
        for x in split_sentense:
            w = re.search(s_reg, x)
            if bool(w) is True:
                s = w.group().replace("\n", " ").replace("  ", "")
                if 'AND' in l[0]:
                    for y in l[0].split('AND'):
                        into_list(flist, y, s)
                else:
                    into_list(flist, l[0], s)

また、スクリプト上で登場人物の表記が少しブレるので修正します。 セリフだけで画角にいないと台本に(S.O.)と言う表記が付くらしいです。
あとミアがCLOSEUPと付くシーンが一件だけあるのでこれも修正。 これらを取り除いてリストに入れるのがinto_listです。

後でアウトプットは出てくるのでコードだけおさらい。

def into_list(l, chara, quote):
    shaping_charactor = chara.replace("\n", "").replace(" ", "").replace("(O.S.)", "")
    if shaping_charactor == 'CLOSEUP・MIA':
       shaping_charactor = 'MIA'
    l.append([shaping_charactor, quote])

何回言ったかのカウンタは別関数でまとめます。

def fcounter(l):
    max_c, max_w = shapes(l)
    clist = []
    for x in l:
        clist.append(x[0])
    c = collections.Counter(clist).most_common()
    for x in c:
        c, w = x
        print(c.ljust(max_c), w)

出力

最後にアウトプット。

まずはfワードを全件出します。
罵詈雑言の嵐です(笑)

>>> fquote(f)
1 YOUNGMAN    I sound like a sensible fxxking man
2 YOUNGMAN    takin' the same fxxkin' risk as when
3 YOUNGMAN    fxxkin' A it worked
4 YOUNGMAN    but a fxxkin' phone
5 YOUNGMAN    lift a fxxkin' finger
6 YOUNGMAN    fxxkin' speak English
7 YOUNGMAN    don't know what it fxxkin' means
8 YOUNGMAN    one of those gook motherfxxkers'
9 YOUNGMAN    fifteen fxxkin' generations
10 YOUNGMAN    counter with a fxxkin' Magnum
11 YOUNGMAN    fxxk it
12 YOUNGMAN    gonna really give a fxxk you're
13 HONEYBUNNY  Any of you fxxkin' pricks move
14 HONEYBUNNY  motherfxxkers
15 JULES       man ・I'm fxxkin' goin'
16 VINCENT     they wouldn't know what the fxxk a
17 VINCENT     they fxxkin' drown 'em in it
18 VINCENT     We should have fxxkin' shotguns
19 JULES       Marsellus fxxked his ass up
20 VINCENT     fxxk her
21 VINCENT     but you're in the same fxxkin'
22 JULES       the same fxxkin' thing
23 JULES       ain't even the same fxxkin' sport
24 JULES       massages ・I'm the foot fxxkin'
25 JULES       fxxk you
26 JULES       fxxk you
27 JULES       motherfxxkin-house
28 JULES       fxxkin' up the
29 JULES       Motherfxxker do that to
30 JULES       I'd kill'a motherfxxker
31 VINCENT     That's what's so fxxkin'
32 VINCENT     fxxkin' Marsellus knew it
33 VINCENT     shoulda known fxxkin' better
34 VINCENT     his fxxkin' wife
35 JULES       You'a smart motherfxxker
36 BRETT       are about how fxxked up things got
37 JULES       English-motherfxxker-can-you-speak-
38 JULES       motherfxxker
39 JULES       Then why did you try to fxxk 'im
40 JULES       Ya tried ta fxxk
41 MARSELLUS   motherfxxker
42 MARSELLUS   that's a hard motherfxxkin' fact of
43 MARSELLUS   motherfxxkers who thought their ass
44 MARSELLUS   fxxkin' wit ya
45 MARSELLUS  fxxk pride
46 VINCENT     man's fxxkin' wife
47 LANCE       But this one's a fxxkin'
48 LANCE       any ol' day of the fxxkin' week
49 LANCE       Coke is fxxkin'
50 LANCE       in a big fxxkin' way
51 VINCENT     You know what some fxxker did to it
52 VINCENT     fxxkin' keyed it
53 LANCE       that's fxxked up
54 VINCENT     out five fxxkin' days ・five days
55 VINCENT     some dickless piece of shit fxxks
56 LANCE       They should be fxxkin' killed
57 LANCE       You don't fxxk
58 VINCENT     What the fxxk is this place
59 VINCENT     That's a pretty fxxkin'
60 VINCENT     but it's pretty fxxkin'
61 MIA         shit the fxxk up for a minute
62 MIA         like it's on fxxking fire)
63 JODY        I thought you told those fxxkin'
64 LANCE       tell this fxxkin' asshole right now
65 LANCE       this fxxkin' late
66 VINCENT     fxxkin' trouble man
67 VINCENT     fxxkin'Doing on me
68 LANCE       fxxkin' joking with you
69 LANCE       be bringing some fxxked up pooh-butt
70 LANCE       Then bite the fxxkin' bullet
71 LANCE       She ain't my fxxkin' problem
72 LANCE       fxxked her up
73 LANCE       your car in my fxxkin' house
74 LANCE       fxxkin' phone ・
75 LANCE       that fxxked up bitch in my house
76 VINCENT     This fxxked up bitch is Marsellus
77 VINCENT     Now if she fxxkin'
78 JODY        What the fxxk's goin' on
79 JODY        here being nobody knows what the fxxk they're doing
80 LANCE       Get the fxxkin' shot
81 VINCENT     Get the fxxkin' shot
82 VINCENT     I'm not fxxkin' stoppin' you
83 LANCE       My black fxxkin' medical book
84 VINCENT     fxxk the
85 VINCENT     Quit fxxkin' around man
86 VINCENT     This ain't a fxxkin' joke man
87 MARSELLUS   this motherfxxker
88 FABIENNE    Shut up fxxk head
89 BUTCH       Yes I've fxxkin' looked
90 BUTCH       What the fxxk do you think I'm doing
91 BUTCH       that was my father's fxxkin'
92 BUTCH       all I gave a fxxk about was my watch
93 BUTCH       Of all the fxxkin' things she coulda
94 MARSELLUS   I'm pretty fxxkin' far from
95 BUTCH       fxxk the bags
96 BUTCH       we gotta hit the fxxkin' road
97 JULES       blowing them all away while they're fxxkin' around
98 VINCENT     Why the fxxk didn't you tell us about
99 JULES       We should be fxxkin' dead right now
100 JULES       We should be fxxkin' dead
101 JULES       Don't you fxxkin' do
102 JULES       What just happened was a fxxkin'
103 VINCENT     Chill the fxxk out
104 JULES       We should be fxxkin' dead now
105 JULES       I want you to fxxkin' acknowledge
106 JULES       eyes are wide fxxkin' open
107 VINCENT     What the fxxk does that mean
108 VINCENT     ・you're fxxkin' freakin' out
109 JULES       What the fxxk's happening
110 JULES       Why the fxxk did you do that
111 JULES       The car didn't hit no motherfxxkin'
112 JULES       motherfxxker
113 JULES       drenched in fxxkin' blood
114 JULES       fxxk we're gonna go
115 JULES       We gotta be real fxxkin' delicate
116 JULES       What the fxxk did you just do to his
117 JULES       didn't look like a fxxkin' Maxie
118 JULES       fxxk it
119 JULES       gourmet fxxkin' shit on us
120 JIMMIE      how fxxkin' good it is
121 JIMMIE      fxxkin' business
122 JIMMIE      fxxkin' divorced
123 JIMMIE      get fxxkin' divorced
124 JIMMIE      ・don't fxxkin' Jimmie me
125 JIMMIE      than get the fxxk out of my house
126 JULES       fxxk up your shit
127 MARSELLUS   No fxxkin' shit she'll freak
128 JULES       motherfxxkin' "ifs
129 JULES       motherfxxker
130 MARSELLUS   I'm on the motherfxxker
131 THEWOLF     the fxxk outta Dodge
132 JULES       motherfxxker's tip-top
133 THEWOLF     you better fxxkin' do it
134 THEWOLF     clean the fxxkin' car
135 JULES       This is some fxxked-up
136 JULES       The motherfxxker who said that
137 VINCENT     it's fxxkin' dangerous to be
138 JULES       mushroom-cloud-layin' motherfxxker
139 JULES       fxxk am I doin' in the back
140 JULES       the motherfxxker should be on brain
141 JULES       that water's fxxkin' cold
142 JULES       motherfxxker
143 THEWOLF     does a fxxkin' thing 'til I do
144 THEWOLF     I drive real fxxkin' fast
145 JULES       motherfxxker
146 JULES       motherfxxkin' charmin' pig
147 JULES       my fxxkin' car keys
148 JULES       That's what's fxxkin' wit' me
149 VINCENT     of fxxkin' zombies
150 VINCENT     gonna be ・a fxxkin' bum
151 VINCENT     Stop fxxkin' talkin' like that
152 HONEYBUNNY  Any of you fxxkin' pricks move
153 HONEYBUNNY  motherfxxkers
154 HONEYBUNNY  Now mean fxxkin' now
155 HONEYBUNNY  do it or fxxking die
156 PUMPKIN     now git your fxxkin' ass
157 JULES       Keep your fxxkin' mouth closed
158 PUMPKIN     unload right in your fxxkin' face
159 HONEYBUNNY  your fxxkin' head off
160 HONEYBUNNY  gonna fxxkin' die bad
161 JULES       And when motherfxxkers
162 JULES       that's when motherfxxkers
163 JULES       fxxkin' fried chicken
164 VINCENT     What the fxxk's goin' on here
165 JULES       Motherfxxker on it
166 JULES       with "Bad Motherfxxker" embroidered on it
167 JULES       That's my bad motherfxxker
168 JULES       shut the fxxk up
169 JULES       say to a motherfxxker 'fore you popped

169件で前回と同じ件数です。OK。

続いて何回言ったかカウンタ。
やはり出演シーンの多い主人公達が多いですね。
本当は同一人物がいるのですが一応ネタバレになるのでここは手つけずです。

>>> fcounter(f)
JULES      59
VINCENT    35
LANCE      19
YOUNGMAN   12
MARSELLUS  9
HONEYBUNNY 8
BUTCH      7
JIMMIE     6
THEWOLF    5
JODY       3
MIA        2
PUMPKIN    2
BRETT      1
FABIENNE   1

OK。
成形もうまいこと効いていていい感じに出力できました!

まとめ

フタを開けてみると成形のコードがほとんどでスクレイピング正規表現は全然やってませんでしたw
やはりセリフを正規表現で抜き出すというのは文章やワードなどかなりのバリエーションがあり難しいです。
前述しましたが結局フレーズ毎に区切ってからワードを抜き出すという方法に落ち着きました。

とりあえず望む結果になったのでよしとします。

さすがに前回より日が経っているだけあって積み残しの解決も出来たしコードの書き方も少しは綺麗に出来たかなと思います。

最近パルプフィクション見てないのですがこの記事書いてたらまた見たくなってしまいました。