python爬虫获取流行小说名


先说说需求:我想做的一个爬虫,它通过爬取“小说推荐吧”里面的所有帖子,从中找到被提到最多的小说,为什么会有这种需求呢,因为我书荒的时候就会去这个贴吧,看哪本小说被推荐的多,我就看哪本了。

要实现这个功能,我碰到的问题主要有以下几个:

  • 如果判断一个词组表示的是一本小说;

  • 采用什么搜索算法;

  • 百度贴吧浏览10页以后的内容需要登录,如何在爬虫里面登录贴吧;

如何判断一个词组表示的是一本小说

对于第一个问题,我是这样解决的,从一个包含很多流行小说的网站上爬取部分小说名字,我是在豆瓣读书上找的,找了几个标签总共2000~3000本小说 的名字,然后将这些小说名字与网页中的文字进行比对,如果有匹配的就表示是一本小说了,虽说无法判断所有小说,但能提取出流行小说就够了。最后匹配了部分 网页发现这种有些问题,比如有小说名叫《战士》或《毒》,这种词出现的太多了,而且大部分时候想表达的意思不是小说名,我最终的处理方案是这些匹配的词后 必须跟特定的符号或词语,比如别人提到“毒写的不错”或“《毒》”,抑或是“毒,战士,活着都可以”,我才认定别人提到的应该是小说名了,当然,这样判断不一定准确,但应该有80%左右是对的。

采用什么搜索算法

多模式匹配,有两种比较流行的算法,AC多模匹配和Wu-Manber算法,前一种似乎不太适合非ascii字符的情形,所有我最终主要采用的是Wu- Manber算法,这种算法的实现思路大概是这样的(因为这不是主要,所以写的比较简单,可以自行百度):比如有 abcdef,ijkhlsd这两个模式串需要在一段文字中进行匹配,我可以设定一个值k=4(该值应该小于最小模式串的长度),和block=2(该值应该小于k值),有两个 表,ShiftTable和HashTable,在ShiftTable中放的是偏移值,对于串abcdef,分别计算ab,bc,cd,的Hash值, 然后在ShiftTable中Hash值对应的地方放上偏移值,比如cd的偏移值是0,bd是1,ab是2,然后HashTable对应偏移为0的地方存 放最开始block大小字符串的哈希值和相应模式串的索引。匹配过程是这样的:字符串从开头中每一个block长度求hash值,找到 ShiftTable中相应地方的偏移值,如果不是0,则将指针前移偏移值个位置,如果是0,则和HashTable中相应地方存放的前缀Hash值进行 比对,如果比对相等,再进行全字匹配。

如何在爬虫里面登录贴吧

根据前人总结的,百度登陆有三个步骤:

  1. Get方式连接https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=false,得到cookie

  2. Get方式连接https://passport.baidu.com/v2/api /?getapi&class=login&tpl=mn&tangram=false,得到的内容中包含一个tocken,解 析出这个tocken的值

  3. Post以下内容:

post_data = {'username':self.usrname,
'password':self.passwd,
'token':self.token,
'charset':'UTF-8',
'callback':'parent.bd12Pass.api.login._postCallback',
'index':'0',
'isPhone':'false',
'mem_pass':'on',
'loginType':'1',
'safeflg':'0',
'staticpage':'https://passport.baidu.com/v2Jump.html',
'tpl':'mn',
'u':'http://www.baidu.com',
'verifycode':'',}

可以通过在最终连接的页面中查找用户名来判断登陆是否成功。
然后是实现过程,我使用的是scrapy爬虫框架,登陆过程和爬取文字代码:

class BdSpider(BaseSpider):
    name="baidu"
    usrname="395318621@qq.com"
    passwd="********"
    usrnick="c395318621"
    allowed_domains = ['baidu.com']
    logined=False
    token=''
    curpage=0
    def __init__(self,keywords,startpage,endpage):
        #self.testurl="http://tieba.baidu.com/f?kw=%s&pn=%d"%(keywords,(startpage-1)*50)
        UrlCode=urllib.quote(keywords)
        self.testurl="http://tieba.baidu.com/f?kw=%s&pn=%d"%(UrlCode,(int(startpage)-1)*50)
        print "[user]",self.testurl
        self.startpage=int(startpage)
        self.endpage=int(endpage)
         
    def start_requests(self):
        cookiename='baidu%s.cookie'%(self.usrname)
        cj=cookielib.LWPCookieJar()
        try:
            cj.revert(cookiename)
            self.logined=True
            print "Has logined before"
        except Exception,e:
            print e
        if self.logined:
            return [Request(url=testurl,callback=self.check_page)]
        else:
            qurl="https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=false"
            return [Request(url=qurl,callback=self.get_cookie,dont_filter=True)]
    def get_cookie(self,response):
        print "int get_cookie"
        qurl="https://passport.baidu.com/v2/api/?getapi&class=login&tpl=mn&tangram=false"
        return [Request(url=qurl,callback=self.get_tocken,dont_filter=True)]
 
    def get_tocken(self,response):
        print "in get_tocken"
        login_tokenStr = """bdPass.api.params.login_token='(.*?)';"""
        login_tokenObj = re.compile(login_tokenStr,re.DOTALL)
        matched_objs=login_tokenObj.findall(response.body)
        print response
        if matched_objs:
            self.token=matched_objs[0]
            print "token:",self.token
            post_data = {'username':self.usrname,
                        'password':self.passwd,
                        'token':self.token,
                        'charset':'UTF-8',
                        'callback':'parent.bd12Pass.api.login._postCallback',
                        'index':'0',
                        'isPhone':'false',
                        'mem_pass':'on',
                        'loginType':'1',
                        'safeflg':'0',
                        'staticpage':'https://passport.baidu.com/v2Jump.html',
                        'tpl':'mn',
                        'u':self.testurl,
                        'verifycode':'',}
                #path = 'http://passport.baidu.com/?login'
            path = 'http://passport.baidu.com/v2/api/?login'
            headers = {
                  "Accept": "image/gif, */*",
                  "Referer": "https://passport.baidu.com/v2/?login&tpl=mn&u=%s"%(self.testurl),
                  "Accept-Language": "zh-cn",
                  "Content-Type": "application/x-www-form-urlencoded",
                  "Accept-Encoding": "gzip, deflate",
                  "User-Agent": "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)",
                  "Host": "passport.baidu.com",
                  "Connection": "Keep-Alive",
                  "Cache-Control": "no-cache"}
            return [FormRequest(url=path,formdata=post_data,headers=headers,callback=self.check_page) ]
 
    def check_page(self,response):
        print "[user]in check_page"
        #self.start_urls.append(self.testurl)
        return [Request(url=self.testurl,callback=self.parse,dont_filter=True)]
 
    def checkLogin(self,response):
        reUser=re.compile(self.usrnick)
        matched=reUser.findall(response.body)
        if matched is not None:
            print "[user] login baidu ok"
            return True
        else:
            print "[user] login failed"
            return False
    def parse(self,response):
        baseurl="http://tieba.baidu.com"
        if self.checkLogin(response):
            response_selector=HtmlXPathSelector(response)
            next_link=response_selector.select('//div[@id="frs_list_pager"]/a[@class="next"]/@href')
            if next_link:
                print "[user]next_link:",next_link.extract()[0]
                link=baseurl+next_link.extract()[0]
                rePage=re.compile(r'pn=(\d+)')
                print "[user]link",link
                p=rePage.findall(link)
                print "rePage",p
                if p:
                    page=int(p[0])/50+1
                    print "dealing with page:%d"%(page)
                    #test
                    if page<=self.endpage and page>=self.startpage:
                        yield Request(url=link,callback=self.parse)
            tie=response_selector.select(u'//div[contains(@class,"threadlist_text threadlist_title j_th_tit  notStarList")]/a[contains(@class,"j_th_tit")]/@href')
            #print "ite",tie
            for item in tie.extract():
                print "[user]tie",item
                yield Request(url=baseurl+item,callback=self.parse_tie)
        else:
            print "[user]checklogin failed"
 
    def parse_tie(self,response):
        print "[user] in parse_tie"
        response_selector=HtmlXPathSelector(response)
        yield self.get_it(response)
 
        szPage=response_selector.select(u'//li[@class="l_reply_num"]/span[@class="red"]/text()')
        if not szPage:
            print "[user] get page num failed"
        else:
            pages=int(szPage.extract()[1])
            #test
            if pages>10:
                pages=10
            for i in range(2,pages+1):
                yield Request(url=response.url+"?pn=%d"%(i),callback=self.parse_others)
 
    def parse_others(self,response):
        print "[user] in parse_others"
        yield self.get_it(response)
 
    def get_it(self,response):
        print "[user] in get_item"
        item=BdItem()
        response_selector=HtmlXPathSelector(response)
        title=response_selector.select(u'//div[@id="j_core_title_wrap"]\
            /div[contains(@class,"core_title")]/h1/text()').extract()[0]
        if len(title)>1:
            print "[user] parse tie title failed"
        item['turl']=response.url
        item['ttitle']=title[0]
        """
        dirRe=re.compile(r"/p/(\d+)")
        pageRe=re.compile(r"pn=(\d+)")
        dirname=dirRe.findall(response.url)[0]
        page=pageRe.findall(response.url)
        if len(page) is 0:
            page=['1']
        try:
            os.mkdir(dirname)
        except Exception as e:
            pass
        """
        content=response_selector.select(u'//div[contains(@class,"d_post_content_main")]/div[contains(@class,"p_content_nameplate")]/cc/div/text()').extract()
        if len(content)==0:
            print "[user] get content failed."
        item['tresponse']=[]
        for text in content:
            item['tresponse'].append(text.encode("utf8"))
        return item

最后,我采用redis数据库来存储爬取的结果,结果的结构大概是这样:

{
    bookname: "xxxx"
    [
        {url:"http:\xxxx.xxx.xx", times: n},#书本出现url和出现次数
        {url:"http:\xxxx.xxx.xx", times: n},
        ...
    ]
}

项目地址 https://github.com/chengxiayan/BaiduSpider/tree/master/bd

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 经过多次尝试,模拟登录淘宝终于成功了,实在是不容易,淘宝的登录加密和验证太复杂了,煞费苦心,在此写出来和大家一起分...
    追不到的那缕风阅读 1,683评论 0 3
  • 第一章 Nginx简介 Nginx是什么 没有听过Nginx?那么一定听过它的“同行”Apache吧!Ngi...
    JokerW阅读 32,624评论 24 1,002
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,275评论 25 707
  • 1、格物。《礼记·大学》:“致知在格物。”格,朱注,至也;格物,穷极事物之理。 文人也要穷极事物之理,...
    旧时月色2017阅读 663评论 0 0