爬取了贴吧的帖子后,再对这些帖子搜索自己想要的信息

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_38282706/article/details/80958610

帖子保存为json文件,格式如下:

tiezi={'title':    '标题',
       'author':   '发帖人',
       'tid':      '帖子的编号',
       'reply_num':'回复数量',
       'last_reply_time':'最后回复时间',
       'last_reply_author':'最后回复人',
       'pages':          '共多少页',
   #帖子里的具体内容,每一层楼
       'post_list': ['1楼',
                     '2楼',
                     '3楼',
                     '4楼',
                     '.....'
                     ]
   }

#每一层楼的list
post_list=[ #1楼
                {
                 'page':      '所在页数',
                 'author':    '发帖人',
                 'floor':     '楼层',
                 'time':'回复时间',
                 'pid':       '该楼层的编号',
                 'content':   '回帖内容(包含了文字、图片、自定义表情)',
                 'voice':     '如果有语音的话,就有',
                 'comment_num':'楼内楼回复数量',
                 'comment_list':    #如果上面不为0,就有
                               ['回复1',
                                '回复2',
                                '回复3']
                 },
                 {'2楼'},
                 {'3楼'}
                ]
#楼内楼
comment_list=[#回复1
         {'page':      '所在楼内楼页数',
          'author':    '发帖人',
          'time':'回复时间',
          'pid':       '该楼层的编号',
          'content':   '回帖内容(包含了文字)',
          'voice':     '如果有语音的话,就有',
          },

          {'回复2'},
          {'回复3'},
        ]

 

现在写了个文件,方便查询

 

'''
3个操作函数:
search_keyword:搜索发帖内容里的某个关键字
search_author: 搜索发帖人回复过的内容
get_content:   某时间后所有的发帖内容(或者是该文件夹内的所有内容)

操作思路:
所有文件的list(file_list)》设定储存文件名(save_word_filename)
》循环每个文件----(search_keyword/search_author/get_content)
        》循环每一行(search_one_file)
                    》进行搜索》再次筛选》设定保存的格式,添加到目标value_dict中----(keyword_paths/author_paths/content_paths)
                    》返回value_dict,储存到文件(save_file)
        》所有文件都遍历保存了,再对存储文件的每一行进行合并,再次保存(Data_Merge)

'''

 

文件如下:

class find_the_word(object):

    def __init__(self,dir_path,tieba):
        self.dir_path=dir_path
        self.tieba=tieba
        self.tieba_dir=dir_path+'\\'+tieba
        self.count=0


    def file_list(self):
        '''根据存放json文件的文件夹,得到json文件的绝对地址组成的list'''
        file_list=[]
        for name in os.listdir(self.tieba_dir):
            file_path=os.path.join(self.tieba_dir, name)
            if os.path.isfile(file_path):
                file_list.append(file_path)
        return file_list


    def save_word_filename(self,search_type,target_word):
        '''设定保存的文件名,如果已经存在,那就删除'''
        save_filename=self.tieba_dir+'~%s:%s.json'%(search_type,target_word)
        if os.path.exists(save_filename):  # 返回文件名,如果存在,删掉文件
            os.remove(save_filename)
        return save_filename



    def search_one_file(self,search_type,one_file,target_word,save_filename):
        '''对每个文件的每一行进行搜索,返回搜索结果,保存到文件'''
        self.count=self.count+1
        print('正搜索第%s个文件'%self.count)
        
        #对当前文件的每一行进行搜索,有符合的就写入保存文件
        with open(one_file, 'r', encoding='utf-8') as f:
            for one_dict in f.readlines():
                the_tiezi=json.loads(one_dict)
                value_dict=search_type(the_tiezi,target_word) #对每一行的dict搜索后,过滤匹配路径,返回value_dict
                if value_dict:
                    self.save_file(save_filename,value_dict)          #把符合条件的value_dict写入文件
                    

    def Data_Merge(self,save_filename):
        '''之前是符合条件的一行一行写入文件,现在提取文件的所有dict/list都合并到一个list,然后保存'''
        all_data=[]
        with open(save_filename, 'r', encoding='utf-8') as f:
            for one_dict in f.readlines():
                data=json.loads(one_dict)
                if isinstance(data,  dict):
                    all_data.append(data)
                elif isinstance(data, list):
                    all_data=all_data+data
        #先删了再保存
        os.remove(save_filename)
        self.save_file(save_filename,all_data)


    def save_file(self,save_filename,search_result):
        '''将每条搜索结果(dict/list)写入对应的文件内'''
        with codecs.open(save_filename, 'a', encoding='utf-8') as f:
            line = json.dumps(search_result, ensure_ascii=False) + "\n"
            f.write(line)

#####  不同之处!
    def search_keyword(self, target_word):
        '''先得到文件夹里的所有json文件路径,再设定保存文件的名字,设定搜索条件 为关键字 循环每个文件,再把搜索内容合并'''
        file_list = self.file_list()  # 方法:得出json文件path组成的list
        save_filename = self.save_word_filename('搜索条件—关键字', target_word)  # 方法:设定搜索后保存的json文件名

        # 根据不同的搜索条件,循环文件夹里的每一个文件
        for one_file in file_list:
            self.search_one_file(self.keyword_paths, one_file, target_word,
                                 save_filename)  # 对每个文件的每一行进行搜索,返回搜索结果,保存到文件

        if os.path.exists(save_filename):
            self.Data_Merge(save_filename)  # 方法:现在爬取的结果dict/list都合并到一个list,保存文件

    def keyword_paths(self,the_tiezi,target_word):
        '''搜索条件:发帖内容content包含关键字,  搜索字典tiezi,过滤匹配路径后,再用target_info处理,返回value_dict
        :param the_tiezi: json文件的一行tiezi字典
        :param target_word: 需要搜索的发帖内容关键字
        :return:
        操作步骤:先用包含匹配查询符合内容的路径path
                 只需发帖内容content,其他不符合的path删掉
                 循环所有路径,把内容添加到value_dict,返回'''
        a=find_path(the_tiezi)
        all_path=a.in_value_path(target_word)

        #只保留 是发言内容的搜索结果路径
        the_path=[]
        for one in all_path:
            key_word = re.findall(r'\[(.*?)\]', one)
            if eval(key_word[-1])=='content':
                the_path.append(one)

        value_dict ={'title': the_tiezi['title'], 'author':the_tiezi['author'], 'tid':the_tiezi['tid'],
                                  'post_list': []}
        
        #搜索结果不为空时,循环,使用target_info,把对应的楼层放进value_dict
        if the_path!=[]:
            for path in the_path:
                self.target_info(path, value_dict, the_tiezi)                
            return value_dict


##### 不同之处!!
    def search_author(self,author):
        '''先得到文件夹里的所有json文件路径,再设定保存文件的名字,设定搜索条件 为 发帖人 循环每个文件,再把搜索内容合并'''
        file_list = self.file_list()  # 方法:得出json文件path组成的list
        save_filename = self.save_word_filename('搜索条件—发帖人', author)  # 方法:设定搜索后保存的json文件名

        # 根据不同的搜索条件,循环文件夹里的每一个文件
        for one_file in file_list:
            self.search_one_file(self.author_paths, one_file, author, save_filename)  # 对每个文件的每一行进行搜索,返回搜索结果,保存到文件

        if os.path.exists(save_filename):
            self.Data_Merge(save_filename)  # 方法:现在爬取的结果dict/list都合并到一个list

    def author_paths(self, the_tiezi, target_word):
        '''搜索条件:发帖人,  搜索字典tiezi,过滤匹配路径后,再用target_info处理,返回value_dict
        :param the_tiezi: json文件的一行tiezi字典
        :param target_word: 需要搜索的发帖内容关键字
        :return:
        操作步骤:先用完全匹配查询 以 发帖人 为关键字的路径path
                 只有当前路径是author,才能留下来
                 循环所有路径,把内容添加到value_dict,返回'''
        a = find_path(the_tiezi)
        all_path = a.the_value_path(target_word)

        # 只保留 是发言内容的搜索结果路径
        the_path = []
        for one in all_path:
            key_word = re.findall(r'\[(.*?)\]', one)
            if eval(key_word[-1]) == 'author':
                the_path.append(one)

        value_dict = {'title': the_tiezi['title'], 'author': the_tiezi['author'], 'tid': the_tiezi['tid'],
                      'post_list': []}

        # 搜索结果不为空时,循环,使用target_info,把对应的楼层放进value_dict
        if the_path != []:
            for path in the_path:
                self.target_info(path, value_dict, the_tiezi)
            return value_dict



    def time_num(self,date_time='无'):
        '''将输入的日期时间转换为时间戳,方便比对大小,若没有输入时间,那就返回0'''
        if re.match(r'\d*-\d*-\d* \d*:\d*', date_time):
            return time.mktime(time.strptime(date_time, "%Y-%m-%d %H:%M"))
        elif re.match(r'\d*-\d*-\d*', date_time):
            return time.mktime(time.strptime(date_time, "%Y-%m-%d"))
        elif date_time == '无':
            return 0

##### 不同之处!!
    def get_content(self, time='无'):
        '''先得到文件夹里的所有json文件路径,再设定保存文件的名字,设定 回帖内容 的 时间限制  循环每个文件,再把搜索内容合并'''
        ###输入的时间格式是对的,才会有效
        if self.time_num(time) is not None:
            file_list = self.file_list()  # 方法:得出json文件path组成的list
            save_filename = self.save_word_filename('回帖内容—某时间后', time)  # 方法:设定搜索后保存的json文件名

            # 根据不同的搜索条件,循环文件夹里的每一个文件
            for one_file in file_list:
                self.search_one_file(self.content_paths, one_file, time,
                                     save_filename)  # 对每个文件的每一行进行搜索,返回搜索结果,保存到文件

                if os.path.exists(save_filename):
                    self.Data_Merge(save_filename)  # 方法:现在爬取的结果dict/list都合并到一个list

    def content_paths(self,dict1,time):
        '''搜索条件:根据设定时间得到帖子内容
        先用key匹配得到所有time的路径,比对time的时间戳,符合要求的路径path留下,然后替换为content,
        循环所有content的路径,得到内容,添加到list里,保存'''
        timestamp=self.time_num(time)
        a = find_path(dict1)
        key_path = a.the_key_path('time')
                #把符合时间的放进time路径的list内
        content_path=[]
        for the_time in key_path:
            time1=eval('dict1'+the_time)
            time1stamp=self.time_num(time1)
            if time1stamp>timestamp:
                content=the_time.replace('time','content')
                content_path.append(content)

        content_list=[]
        for path in content_path:
            the_content=eval('dict1'+path)
            content_list.append(the_content)

        if content_list != [] :
            return content_list


    def target_info(self, path, value_dict, dict1):
        '''分别把符合条件的 楼层的回复、楼内楼的回复 放进value_dict各自对应的list
        :param path:      find_path后匹配的路径
        :param value_dict: 存放查询信息的字典
        :param dict1:     所被查询的总字典
        操作步骤:先把搜索后得出的路径path匹配好,
                 判断path是由几部分组成的:
                 ···3个,这是楼层,判断这个楼层没有记录过:···没有,就重新设定这个楼层,整个添加到the_post_list
                                                       ···有,就锁定,判断有几个key:~~~如果只有4个,说明先记录了楼内楼,得把楼层内容content补上
                 ···5个,这是楼内楼,判断这个楼层没有记录过:···没有,就重新设定这个楼层,添加楼内楼后整个添加到the_post_list
                                                        ···有就锁定,然后添加楼内楼

        '''
        '''存在被搜索的楼层规格如下:
                   1.只有楼层-----       {floor、page、url、author、content、voice、time、comment_list:[]}
                   2.有楼层又有楼内楼----{floor、page、url、author、content、voice、time
                                         comment_list:[{author、content、voice、time、comment_page},
                                                        ]}
                   3.只有楼内楼----      {floor、page、url、
                                         comment_list:[{author、content、voice、time、comment_page},
                                                        ]}
                   '''
        a = re.findall(r'\[(.*?)\]', path)  # 匹配时3个,说明是楼层,5个就是楼内楼
        #锁定存放查询信息的字典存放楼层的list
        the_post_list=value_dict['post_list']

        #这是楼层回复,格式['post_list'][109]['content']
        if len(a) == 3:
            #锁定总字典dict1当前的楼层、url
            target_post_list = eval('dict1' + "['post_list'][%s]" % a[1])
            url='https://tieba.baidu.com/p/%s?pn=%d' % (dict1['tid'], target_post_list['page'])
            
            # 当前存放回复的楼层的floor集合list
            floor_list = [post_dict['floor'] for post_dict in the_post_list]
            # 当前楼内楼所在的楼层
            the_floor = eval('dict1' + "['post_list'][%s]" % a[1]).get('floor')

            # 判断这个楼层没有记录过,···没有,就重新设定这个楼层,整个添加到the_post_list
            #                    ···有,就锁定,判断有几个key:~~~如果有7个,说明已经记录过,不用动
            #                                              ~~~如果只有4个,说明先记录了楼内楼,得把楼层内容content补上
            if the_floor not in floor_list:
                post_dict = copy.deepcopy(target_post_list)
                post_dict['url']=url
                if 'comment_list' in post_dict :post_dict.pop('comment_list')
                post_dict.pop('comment_num')
                the_post_list.append(post_dict)
            else:
                if len(target_post_list)==4:
                    # 锁定存放查询信息的字典value_dict的当前楼层
                    post_dict=[the_post for the_post in the_post_list if the_post['floor'] == the_floor][0] 
                    post_dict['author']=target_post_list['author']
                    post_dict['content'] = target_post_list['content']
                    post_dict['voice'] = target_post_list['voice']
                    post_dict['time'] = target_post_list['time']
                    

        #这是楼内楼回复 格式:['post_list'][117]['comment_list'][0]['content']
        elif len(a) == 5:
            #锁定总字典dict1当前楼内楼
            comment_dict = eval('dict1' + "['post_list'][%s]['comment_list'][%s]" % (a[1], a[3]))
        
            #当前存放回复的楼层的floor集合list
            floor_list=[post_dict['floor'] for post_dict in the_post_list ]
            #当前楼内楼所在的楼层
            the_floor= eval('dict1' + "['post_list'][%s]" % a[1]).get('floor')

            #判断这个楼层没有记录过,···没有,就重新设定这个楼层,添加楼内楼后整个添加到the_post_list
            #                    ···有就锁定,然后添加楼内楼
            if the_floor not in floor_list:
                page= eval('dict1' + "['post_list'][%s]" % a[1]).get('page')
                url = 'https://tieba.baidu.com/p/%s?pn=%d' % (dict1['tid'], page)
                post_dict={'floor':the_floor,'page':page,'url':url,'comment_list':[]}
                post_dict['comment_list'].append(comment_dict)
                the_post_list.append(post_dict)
            else:
                # 锁定存放查询信息的字典value_dict的当前楼层
                post_dict=[the_post for the_post in the_post_list if the_post['floor'] == the_floor][0]
                if post_dict.get('comment_list') is None:post_dict['comment_list']=[]
                if comment_dict not in post_dict['comment_list']:
                    post_dict['comment_list'].append(comment_dict)
                
            
if __name__ == '__main__':
    time1=time.time()
    dir_path = r'C:\Users\Administrator\Desktop\tieba1' #设定保存了帖子的总文件夹
    a=find_the_word(dir_path,'国际米兰')#指定该贴吧 文件夹
    a.search_keyword('肠胃')             #搜索关键字——肠胃
    a.search_author('秋天的小绵羊3')       #搜索发帖人——秋天的小绵羊3
    a.get_content()                     #某时间后:2018-10-22 的发帖内容

    time2=time.time()-time1
    print(time2)

 

搜索后保存的文件:

 

内容分别如下:

关键字:肠胃

 

发帖人:秋天的小绵羊3

 

某时间后:2018-10-22 的所有内容:

展开阅读全文

对一篇帖子的疑惑

08-02

c++输入缓冲区处理(转)2007-12-12 21:29源代码为:#include rn#include using namespace std;int main()rnrnstring world;while (cin >> world)rncout << world << endl;rnrnreturn 0;rn此程序有个问题,在windows操作系统下用ctrl+z结束输入后程序处于等待状态,需要再次按下回车才可以正常结束。如:输入abcd^Z不结束,要再次输入^Z,回车,回车才结束。原因分析如下:输入缓冲是行缓冲。当从键盘上输入一串字符并按回车后,这些字符会首先被送到输入缓冲区中存储。每当按下回车键后,cin.get() 就会检测输入缓冲区中是否有了可读的数据。cin.get() 还会对键盘上是否有作为流结束标志的 Ctrl+Z 或者 Ctrl+D 键按下作出检查,其检查的方式有两种:阻塞式以及非阻塞式。rnrn阻塞式检查方式指的是只有在回车键按下之后才对此前是否有 Ctrl+Z 组合键按下进行检查,非阻塞式样指的是按下 Ctrl+D 之后立即响应的方式。如果在按 Ctrl+D 之前已经从键盘输入了字符,则 Ctrl+D的作用就相当于回车,即把这些字符送到输入缓冲区供读取使用,此时Ctrl+D不再起流结束符的作用。如果按 Ctrl+D 之前没有任何键盘输入,则 Ctrl+D 就是流结束的信号。rnrnWindows系统中一般采用阻塞式检查 Ctrl+Z、Unix/Linux系统下一般采用非阻塞式的检查 Ctrl+D。楼主是在Windows系统下,因此使用阻塞式的 Ctrl+Z 来标识流的结束。rnrn这种阻塞式的方式有一个特点:只有按下回车之后才有可能检测在此之前是否有Ctrl+Z按下。还有一个特点就是:如果输入缓冲区中有可读的数据则不会检测Ctrl+Z(因为有要读的数据,还不能认为到了流的末尾)。还有一点需要知道:Ctrl+Z产生的不是一个普通的ASCII码值,也就是说它产生的不是一个字符,所以不会跟其它从键盘上输入的字符一样能够存放在输入缓冲区。明白了这几点之后就可以来解释这个问题了。rnrn从键盘上输入abcd^z 加回车之后在Windows系统上是这样处理的:由于回车的作用,前面的 abcd 等字符被送到输入缓冲区(注意:上面说过了,^z不会产生字符,所以更不会存储到输入缓冲区,缓冲区中没有 ^z 的存在)。这时,cin.get() 检测到输入缓冲区中已经有数据存在(因此不再检查是否有 ^z 的输入),于是从缓冲中读取相应的数据。如果都读取完了,则输入缓冲区重新变为空,cin.get() 等待新的输入。可见,尽管有 ^z 按下,但是由于在此之前还有其它输入字符(abcd),所以流也不会结束。rnrn因此,输入流结束的条件就是:^z 之前不能有任何字符输入(回车除外),否则 ^z 起不到流结束的作用。故输入:abcd^Z后只是把abcd读入缓冲,这里的^Z的作用就是让abcd读入缓冲,并没有起到结束文件的作用。再次输入^Z,回车把^Z读入缓冲。然后下一个回车系统才判断上次输入的^Z。故结束循环。 rnrn我想很多人都看过这个东西:rn但是我有很多东西都没弄懂rn1.按照他的说法那么当我运行后什么都不输入直接ctrl+z那就不能结束了吗?(缓冲区里没东西)为什么还是要输入ctrl+z后按回车在ctrl+z。而且当我输入一串字符后按回车就在屏幕上显示出来了(我认为此时输入缓冲区已空),那么我输入ctrl+z就应该结束了啊。但为什么还要回车再输入ctrl+z;rn2.我对回车或者'\n'是干什么的也模糊了,如果换成#include rn#include using namespace std;int main()rnrnstring world;while (cin >> world)rncout << world << '\n';rnrnreturn 0;rn输入一串字符串之后,我用了回车,为什么就能在显示器上显示我输入的啊(程序没有结束所以没有刷新缓冲区[b]我用的是\n[/b])rn3.当我输入一串字符后按下回车后while(cin >> world)这句并没有执行完啊(cin>>world为真),所以我觉得不会执行下一句,但为什么还是会输出字符rn本人比较笨,请大家说得详细点。谢了 论坛

对elegent87帖子的疑惑?

08-02

#includern#includern#includernrnusing namespace std;rnrnclass student rnrnpublic:rn student();rn Input(vector& stu);rn student(const string& nam,const string& num,int sco):name(nam),number(num),score(sco);rn friend istream& operator>> (istream& in,student& stu)//是不是成员函数,要是的话为什么不能直接调用其数据成员?rn rn cout<<"请输入姓名: ";rn in>>stu.name;rn cout<<"请输入学号: ";rn in>>stu.number;rn cout<<"请输入成绩: ";rn in>>stu.score;rn return in;rn rn friend ostream& operator <<(ostream& out,student& stu)rn rn out<& stu);rnstring student::Get_number()rnrn return number;rnrnrnrnrnvoid Input(vector& stu)rnrn student s;//rn char ch;rn dorn rn cin>>s; //怎么去关联类?怎么去执行friend istream& operator>> (istream& in,student& stu)rn//这样的函数了?为什么没有继续执行friend ostream& operator?rn stu.push_back(s);rn rn cout<<"继续录入学生信息吗?(y/n): ";rn cin>>ch;rn rn while(ch=='y');rnrnrnvoid Show(vector& stu)rnrn vector::iterator iter;rn cout<<"姓名\t"<<"学号\t"<<"成绩"<& stu)rnrn char ch;rn string num;rn bool result=false;rn vector::iterator iter;rn if(stu.size()==0)rn rn cout<<"没有学生的信息!";rn Menu(stu);rn rn dorn rn cout<<"请输入你要删除学生的学号: ";rn cin>>num;rn for(iter=stu.begin();iter!=stu.end();++iter)rn if(iter->Get_number()==num)rn rn cout<<"找到该学生!正在删除......"<>ch;rn while(ch=='y');rnrnrnrnrnrnvoid Menu(vector& stu)rnrn char ch;rn dorn rn cout<<"\t\t\t1.录入学生信息"<>ch;rn switch(ch)rn rn case '1':Input(stu);break;rn case '2':Show(stu);break;rn case '3':Delete(stu);break;rn case '0':cout<<"\t\t\tGoodbye!"< stu;rn Menu(stu);rn return 0;rnrn另外就是能不能把input, show ,delete 函数作为类的成员函数,在类里面声明,类外定义? 论坛

没有更多推荐了,返回首页