Scrapy抓取关键字(支持百度、搜狗等)

image.png

前几天由于工作需要,需要抓取一个特定的关键字来提取百度中搜索的结果,并保留50页的数据存入数据库或者.csv文件中。(每天抓取一次)

1.项目需要环境安装

1)scrapy+selenium+chrome(phantomjs)
关于爬虫所依赖环境的安装,我前面已经介绍过了。大家可以参考https://www.jianshu.com/p/1890e9b3ba37这篇文章我的详细介绍即可。
2)mysql+redis安装 数据库安装可查阅百度(很简单)

2.项目开发流程介绍

我们需要模拟用户行为在浏览器的输入框输入指定的关键字,并模拟点击获取想要的数据,将此页面展示的数据进行保存、过滤,并模拟翻页,抓取此关键字的前50页展示,获取我们想要的数据,存入.csv文件或者redis数据库中,以便后面数据分析使用。

3.开发代码详解

1)创建一个scrapy项目
scrapy startproject keyword_scrawl
scrapy genspider redistest baidu.com

  1. 代码中各文件介绍
    settings.py是一个总配置文件:

BOT_NAME : 工程名字

SPIDER_MODULES:

NEWSPIDER_MODULE:

下面module的配置路径

pipelines.py 这个是一个跟数据存储相关的文件

middlewares.py 可以自定义,让scrapy更加可控

items.py 这个文件有点类似于 django中的一个form,定义了数据保存的格式

,但是它要比django的form应用简单,因为它的字段是十分单一的。
spider文件夹:这个文件夹中存放的是具体的某个网站的爬虫.通过命令行,我们可以创建出属于自己的一个spider。

4.spider代码详解

def make_requests_from_url(self, url):
        if self.params['st_status'] == 1:
            return Request(url, meta={'keyword': self.keyword, 'engine':self.sousu, 'phantomjs':True})
        else:
            return Request(url)

首先修改spider中make_requests_from_url函数,添加一个判断,当st_status==1时,我们在返回request对象时,添加一个meta,并在meta中携带我们想要搜索的关键和需要访问的浏览器地址。以及启动phantomjs的指令。

class PhantomJSMiddleware(object):
    @classmethod
    def process_request(cls, request, spider):
        if request.meta.has_key('phantomjs'):
            keyword = request.meta.get('keyword')
            dcap = dict(DesiredCapabilities.PHANTOMJS)
            dcap["phantomjs.page.settings.userAgent"] = ('Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0')
            dcap["phantomjs.page.settings.AcceptLanguage"] = ('zh-CN,zh;q=0.9')
            driver = webdriver.PhantomJS(desired_capabilities=dcap)
            driver.set_page_load_timeout(100)
            driver.set_script_timeout(15)
            try:
                driver.get(request.url)
                if request.meta.get('engine') == 1:
                    driver.find_element_by_id("query").send_keys(keyword)
                    driver.find_element_by_class_name("swz").click()
                elif request.meta.get('engine') == 2:
                    driver.find_element_by_id("kw").send_keys(keyword)
                    driver.find_element_by_id("su").click()
                time.sleep(2)
                final_url = driver.current_url
                if final_url != request.url:
                    fullurl = final_url
                else:
                    fullurl = request.url
                content = driver.page_source.encode('utf-8','ignore')
                driver.quit()
                return HtmlResponse(fullurl, encoding='utf-8', body=content, request=request)
            except Exception, e:
                driver.quit()
                print e

其次修改middlewares中间件中的类方法process_request,此方法默认携带request和spider对象,在我们刚刚修改的make_requests_from_url方法中。我们可以在这里处理前面make_requests_from_url函数返回的Request请求,然后加载selenium、phantomjs,从而获取我们需要访问的浏览器和关键字,此代码会模拟用户的行为去获取关键字内容,然后将页面的内容返回给scrapy.http 中的HtmlResponse对象。这样我们就可以在spider 内的parse函数中获取到刚刚抓取的内容response.body。

    # 判断页面的返回状态
        if int(response.status) >= 200 and int(response.status) < 400:
            if not self.params['redis_key']:
                a_list = response.xpath('//h3/a/@href').extract()
                for url in a_list:
                    if url.startswith('http://') != True and url.startswith('https://') !=True:
                        url = response.urljoin(url)
                    yield scrapy.Request(url=url, meta={'url':response.url}, callback=self.pang_bo, dont_filter=True)

                if response.meta.has_key('page') != True and self.sousu == 2:
                    flag = 1
                    for next_url in response.xpath('//div[@id="page"]/a/@href').extract():
                        if next_url.startswith('http://') != True and next_url.startswith('https://') !=True:
                            nextUrl = self.start_urls[0] + next_url
                            regex = 'pn=(\d+)'
                            page_number = re.compile(regex).search(nextUrl).group(1)
                            if page_number and flag:
                                flag = 0
                                # 抓取前50页
                                for page in range(10,500,10):
                                    next_page = 'pn=' + str(page)
                                    old_page = re.compile(regex).search(nextUrl).group()
                                    nextUrl = nextUrl.replace(old_page, next_page)
                                    yield scrapy.Request(url=nextUrl, meta={'page':page}, callback=self.parse)

上面的代码就是获取刚刚网页中展示的每一个搜索结果,并获取到页面规律,模拟翻页50页,将50页的内容全部递交给self.pang_bo函数进行处理。在此做了一个页面去重!

    # 处理item
    def parse_text(self, response):

        item = {}
        try:
            father_url = response.meta["url"]
        except:
            father_url = "''"
        try:
            item['title'] = response.xpath('//title/text()').extract_first().replace('\r\n','').replace('\n','').encode('utf-8')
        except:
            item['title'] = "''"
        item['url'] = response.url
        item['domain'] = ''
        item['crawl_time'] = time.strftime('%Y%m%d%H%M%S')
        item['keyword'] = ''
        item['Type_result'] = ''
        item['type'] = 'html'
        item['filename'] = 'yq_' + str(int(time.time())) + '_0' + str(rand5())+'.txt'
        item['referver'] = father_url
        item['like'] = ''
        item['transpond'] = ''
        item['comment'] = ''
        item['publish_time'] = ''
        return item
    def pang_bo(self, response):
        # 过略掉百度网页
        if 'baidu.com' not in response.url and 'ctrip.com' not in response.url and 'baike.com' not in response.url:
            item = self.parse_text(response)
            content = soup_text(response.body)
            if len(content) > 3000:
                content = content[:3000]
            #elif len(content) == 0:
                #yield scrapy.Request(url=response.url, meta={'url':response.url, 'phantomjs':True}, callback=self.pang_bo)
            body = item['url']+','+item['crawl_time']+','+item['title'].replace(',','') +','+content+'\n'
            if '正在进入' == item['title']:
                file_name = os.path.join(self.filetxt,time.strftime('%Y%m%d%H')+'keyword.csv')
                with open(file_name, 'a') as b:
                    b.write(body)
            else:
                filename = os.path.join(self.filetxt,time.strftime('%Y%m%d%H')+'.csv')
                with open(filename, 'a') as f:
                    f.write(body)

# 过滤网页源代码
def soup_text(body):
    try:
        soup = BeautifulSoup(body, 'lxml')
        line = re.compile(r'\s+')
        line = line.sub(r'', soup.body.getText())
        p2 = re.compile(u'[^\u4e00-\u9fa5]')  # 中GDAC\u4e00\u9fa5
        str2 = p2.sub(r'', line)
        outStr = str2.strip(',')
    except:
        outStr = ''
    return outStr

这个代码主要过略掉一些不需要的网站,然后提取item字段,和页面body(这里过滤了源码)然后将获取到的内容保存到.csv文件中。这只是一个简简单单的爬虫,反爬的话就设置settings如下:

LOG_STDOUT = True    # 将进程所有的标准输出(及错误)将会被重定向到log中(为了方便调试)
DOWNLOAD_DELAY=0.25    # 下载延时设置 单位秒
DOWNLOAD_TIMEOUT = 60  # 下载超时设置(单位秒)
CONCURRENT_ITEMS = 200  # 同时处理的itmes数量
CONCURRENT_REQUESTS = 16 # 同时并发的请求

今天的代码到这里就介绍完了,还是想说:"做一个爱分享的程序员,大家有什么疑问欢迎留言",如果觉得我的文章可以,欢迎关注和点赞。谢谢各位!

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