用Python处理四亿条推特 -- 提速方法

5 投票
5 回答
3583 浏览
提问于 2025-04-17 09:42

我有4亿条推文(其实我觉得差不多有4.5亿,不过没关系),格式是这样的:

T    "timestamp"
U    "username"
W    "actual tweet"

我想先把它们写入一个文件,格式是“用户名 \t 推文”,然后再加载到数据库里。问题是,在加载到数据库之前,我需要做几件事:

  1. 处理推文,去掉RT@[名字]和网址
  2. 从“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 个回答

3

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
3

在你用性能分析工具检查之前,很难知道哪些地方需要改进。不过,我建议你关注一下创建和运行正则表达式的地方,这里可能是最容易导致慢的地方。

因为你的文件有特定的格式,使用 lex 和 yacc 的组合可能会让速度大幅提升。如果你使用 python lex+yacc,虽然速度提升可能没有那么明显,但你就不用去碰 C 语言的代码了。

如果觉得这个方法太复杂,可以试试在开始循环之前先 编译 正则表达式。你还可以让文件的不同部分由独立的工作线程或进程来处理。

不过,还是要强调一下,使用性能分析工具可以帮助你找到真正导致性能瓶颈的地方。先找出问题所在,再看看这些方法能否解决它。

7

这就是multiprocessing的用途。

你有一个可以分成很多小步骤的流程。每个步骤都是一个Process,它从管道中获取一个项目,进行小的处理,然后把中间结果放到下一个管道里。

你会有一个Process,它每次读取原始文件的三行,然后把这三行放进一个管道里。就这么简单。

接下来,你会有一个Process,它从管道中获取一个(T,U,W)的三元组,清理用户行,然后把它放到下一个管道里。

等等,等等。

一开始不要设置太多步骤。读取 - 转换 - 写入是一个不错的开始,这样可以确保你理解multiprocessing模块。之后,你可以通过实践来找出最佳的处理步骤组合。

当你启动这个程序时,它会生成多个可以相互通信的顺序进程,这些进程会消耗你所有的CPU资源,但能相对快速地处理文件。

通常来说,更多的进程同时工作会更快。不过,最终会因为操作系统的开销和内存限制而达到一个上限。

撰写回答