抓取某位名人的演讲内容,通过对演讲内容用词的分析,按照这篇演讲的风格模拟生成一段文字。
整体思路是从网上抓取内容后对格式进行整理,提取文章中的所有单词。然后在统计每个单词后面衔接的单词的频次。例如:I believe I can fly, I believe I can touch the sky. 这句话中,单词I后面出现的单词是believe 2次,can 2次,而can后面出现的单词是fly 1次,touch 1次, 而believe后面出现的单词是I两次。所以,如果用I开头模仿这个风格造一个三个词的句子,很有可能是I can fly或I can touch或I believe I。
用Scrapy抓取http://pythonscraping.com/files/inaugurationSpeech.txt这篇文章,该文是美国第九任总统威廉.亨利.哈里森的就职演说。1840年,威廉.亨利.哈里森在68岁高龄时竞选获胜,哈里森就职那天正遇上寒流,凛冽寒风中,哈里森用了近两个小时宣读他的就职演说。他的演讲加上几位其他官员的讲话,使参加者长时间领略了华盛顿的寒冷。几天之后,这位老人就得了肺炎,在32天后逝去,成为美国第一位在任内去世的总统也是历史上在位时间最短的总统。
创建蜘蛛类并将蜘蛛命名为babyspider6
class MySpider(scrapy.Spider):
name = 'babyspider6'
重写start_requests方法并传入要抓取的链接:
def start_requests(self):
url = 'http://pythonscraping.com/files/inaugurationSpeech.txt'
yield scrapy.Request(url, callback=self.parse)
在重写parse方法之前先写一些辅助性的方法。
还用上面的例子来说,对于I believe I can fly, I believe I can touch the sky. 这件话在拆分词和统计衔接词的时候会使用字典来组织内容:
{'I':{'believe':2,'can':2},
'believe':{'I':2},
'can':{'fly':1,'touch':1},
...
}
所以写一个buildWordDict方法来生成这样的字典:
def buildWordDict(self, text):
text = text.replace('\n', '')
text = text.replace('"', '')
punc = [',', '.', ';', ':']
for x in punc:
text = text.replace(x, ' ' + x + ' ')
words = text.split()
words = [word for word in words if word != '']
wordsDict = {}
for x in range(1, len(words)):
if words[x - 1] not in wordsDict:
wordsDict[words[x - 1]] = {}
if words[x] not in wordsDict[words[x - 1]]:
wordsDict[words[x - 1]][words[x]] = 0
wordsDict[words[x - 1]][words[x]] += 1
return wordsDict
因为较长的一句话中也会包含标点符号进行断句,所以代码中对标点符号都进行了处理,在标点符号的前后加上空格,方便使用split函数对全文进行拆分。
在获得了这样的单词后,我们就要进行单词的筛选。筛选的方案有很多种,但总的原则是应该让频次高的单词更有可能被选中。这里采用的方式是将频次作为分数(权重),然后利用分数进行筛选。
继续用上面的例子:I后面的衔接单词的总分数是4分,然后利用python的randint生成一个1~4的随机数value,依次用value减去衔接词的分数,当value的值<=0的时候,就将这个衔接词返回。如果生成的value为1或2则返回believe,如果生成的value为3或4则返回can。
def wordlistSum(self, wordlist):
sum = 0
for k, v in wordlist.items():
sum += v
return sum
def getRandomWord(self, wordList):
value = randint(1, self.wordlistSum(wordList))
for k, v in wordList.items():
value -= v
if value <= 0:
return k
现在可以重写parse方法接收演讲正文并进行处理生成一句话:
def parse(self, response):
text = response.text
wordsDict = self.buildWordDict(text)
length = int(getattr(self, 'len', 20))
chain = ''
currentWord = 'I'
for x in range(length):
chain += currentWord + ' '
if currentWord == '.': break
currentWord = self.getRandomWord(wordsDict[currentWord])
with open('speech.txt', 'w') as f:
f.write(chain)
我们使用单词I作为一句话的开头,默认句子中单词的个数为20个,可以通过Scrapy的命令行提供不同的单词个数。拼句子的时候如果发现了句号或者单词个数达到了length,则停止句子的拼接。并将拼接的结果写入speech.txt中保存。
scrapy crawl babyspider6 -a len=30
得到的句子是:
I know the security of the public finances ; and manly examination of power of their engagements into which thousands of that patriot from whose coming was created .
百度翻译一下:我知道公共财政的安全性,也很有男子气概地检查了他们交战的力量,成千上万的爱国者从中诞生。