用Python处理四亿条推特 -- 提速方法
我有4亿条推文(其实我觉得差不多有4.5亿,不过没关系),格式是这样的:
T "timestamp"
U "username"
W "actual tweet"
我想先把它们写入一个文件,格式是“用户名 \t 推文”,然后再加载到数据库里。问题是,在加载到数据库之前,我需要做几件事:
- 处理推文,去掉RT@[名字]和网址
- 从“http://twitter.com/用户名”中提取出用户名。
我正在使用Python,这段代码是这样的。请告诉我怎么能让它运行得更快一些 :)
'''The aim is to take all the tweets of a user and store them in a table. Do this for all the users and then lets see what we can do with it
What you wanna do is that you want to get enough information about a user so that you can profile them better. So , lets get started
'''
def regexSub(line):
line = re.sub(regRT,'',line)
line = re.sub(regAt,'',line)
line = line.lstrip(' ')
line = re.sub(regHttp,'',line)
return line
def userName(line):
return line.split('http://twitter.com/')[1]
import sys,os,itertools,re
data = open(sys.argv[1],'r')
processed = open(sys.argv[2],'w')
global regRT
regRT = 'RT'
global regHttp
regHttp = re.compile('(http://)[a-zA-Z0-9]*.[a-zA-Z0-9/]*(.[a-zA-Z0-9]*)?')
global regAt
regAt = re.compile('@([a-zA-Z0-9]*[*_/&%#@$]*)*[a-zA-Z0-9]*')
for line1,line2,line3 in itertools.izip_longest(*[data]*3):
line1 = line1.split('\t')[1]
line2 = line2.split('\t')[1]
line3 = line3.split('\t')[1]
#print 'line1',line1
#print 'line2=',line2
#print 'line3=',line3
#print 'line3 before preprocessing',line3
try:
tweet=regexSub(line3)
user = userName(line2)
except:
print 'Line2 is ',line2
print 'Line3 is',line3
#print 'line3 after processig',line3
processed.write(user.strip("\n")+"\t"+tweet)
我以以下方式运行了代码:
python -m cProfile -o profile_dump TwitterScripts/Preprocessing.py DATA/Twitter/t082.txt DATA/Twitter/preprocessed083.txt
这是我得到的输出:(警告:这个输出挺大的,我没有过滤掉小的值,因为我觉得它们可能也有意义)
Sat Jan 7 03:28:51 2012 profile_dump
3040835560 function calls (3040835523 primitive calls) in 2500.613 CPU seconds
Ordered by: call count
ncalls tottime percall cumtime percall filename:lineno(function)
528840744 166.402 0.000 166.402 0.000 {method 'split' of 'str' objects}
396630560 81.300 0.000 81.300 0.000 {method 'get' of 'dict' objects}
396630560 326.349 0.000 439.737 0.000 /usr/lib64/python2.7/re.py:229(_compile)
396630558 255.662 0.000 1297.705 0.000 /usr/lib64/python2.7/re.py:144(sub)
396630558 602.307 0.000 602.307 0.000 {built-in method sub}
264420442 32.087 0.000 32.087 0.000 {isinstance}
132210186 34.700 0.000 34.700 0.000 {method 'lstrip' of 'str' objects}
132210186 27.296 0.000 27.296 0.000 {method 'strip' of 'str' objects}
132210186 181.287 0.000 1513.691 0.000 TwitterScripts/Preprocessing.py:4(regexSub)
132210186 79.950 0.000 79.950 0.000 {method 'write' of 'file' objects}
132210186 55.900 0.000 113.960 0.000 TwitterScripts/Preprocessing.py:10(userName)
313/304 0.000 0.000 0.000 0.000 {len}
我去掉了一些非常小的值(比如1、3等等)
请告诉我还可以做哪些其他的改动。谢谢!
5 个回答
str.lstrip 可能没有按照你预期的那样工作:
>>> 'http://twitter.com/twitty'.lstrip('http://twitter.com/')
'y'
来自文档的说明:
S.lstrip([chars]) -> string or unicode
Return a copy of the string S with leading whitespace removed.
If chars is given and not None, remove characters in chars instead.
If chars is unicode, S will be converted to unicode before stripping
在你用性能分析工具检查之前,很难知道哪些地方需要改进。不过,我建议你关注一下创建和运行正则表达式的地方,这里可能是最容易导致慢的地方。
因为你的文件有特定的格式,使用 lex 和 yacc 的组合可能会让速度大幅提升。如果你使用 python lex+yacc,虽然速度提升可能没有那么明显,但你就不用去碰 C 语言的代码了。
如果觉得这个方法太复杂,可以试试在开始循环之前先 编译 正则表达式。你还可以让文件的不同部分由独立的工作线程或进程来处理。
不过,还是要强调一下,使用性能分析工具可以帮助你找到真正导致性能瓶颈的地方。先找出问题所在,再看看这些方法能否解决它。
这就是multiprocessing的用途。
你有一个可以分成很多小步骤的流程。每个步骤都是一个Process
,它从管道中获取一个项目,进行小的处理,然后把中间结果放到下一个管道里。
你会有一个Process
,它每次读取原始文件的三行,然后把这三行放进一个管道里。就这么简单。
接下来,你会有一个Process
,它从管道中获取一个(T,U,W)的三元组,清理用户行,然后把它放到下一个管道里。
等等,等等。
一开始不要设置太多步骤。读取 - 转换 - 写入是一个不错的开始,这样可以确保你理解multiprocessing
模块。之后,你可以通过实践来找出最佳的处理步骤组合。
当你启动这个程序时,它会生成多个可以相互通信的顺序进程,这些进程会消耗你所有的CPU资源,但能相对快速地处理文件。
通常来说,更多的进程同时工作会更快。不过,最终会因为操作系统的开销和内存限制而达到一个上限。