由于项目要使用新闻,大量的数据所以想到了python的scrapy 下面大致讲一讲如何安装使用,直到整个新闻采集模块完成,网址什么的自己找 这里只是示范这里的项目环境是python 2.66 centos
1.Scrapy安装
1.1查看python版本因为官网上已经写清楚了要求2.7所以第一步升级Python版本
1.下载
wget http://python.org/ftp/python/2.7.3/Python-2.7.3.tar.bz2
2.解压编译
1.tar -jxvf Python-2.7.3.tar.bz2
2.进入到Python-2.7.3./configure
然后make all再然后make install
3.然后查看版本信,我这个是安装到了/usr/local/bin/
执行/usr/local/bin/python2.7 -v
4.然后建立软链接
mv /usr/bin/python /usr/bin/python2.6.6
ln -s /usr/local/bin/python2.7 /usr/bin/python
5.这时候输入python 就会看到已经是python2.7了
然后这个时候发现yum 不能用了 要改下 /usr/bin/yum 把第一行的!/usr/bin/python改成 !/usr/bin/python2.6.6
2. pip在centos下默认是不行的yum search pip 是找不到的 所以yum install pip 根本就不行
然后所以这里要自己装了
将这个网页保存https://bootstrap.pypa.io/get-pip.py 然后python get-pip.py 如果此文件下载不下来的可以留言 毕竟我下了好久 还好是任务自动下载的
3. 然后
pip install Scrapy
这个可能会有一堆错误 我是安装了
yum update libxml2-devel libxslt-devel python-devel
其他错误就自己谷歌一下解决吧
4.测试
执行scrapy 如果有提示出来说明已经安装成功了,如果没有就说明并没有安装成功
2.使用
1.这时候在tutorial下面已经可以执行scrapy crawl dmoz已经可以开始抓取了会看到抓取的信息。
3.其他
在sprider文件夹下面有一个dmoz_spider.py文件,这里主要是使用了里面的一些方法,如果要针对自己的网站进行采集需要在这里修改或者新建.
name = "dmoz" allowed_domains = ["dmoz.org","163.com"] ''' start_urls = [ "http://news.163.com/" ]
我这里要采集的是163的新闻,这里只是做一个演示,如果要用他们家新闻可能需要沟通版权方面的问题。 这个allowed_domains是要采集的域名,如果这里没有写在后面的采集过程中会有错误出现。要采集163的新闻需要自己去解析。主要的是在def parse(self, response)这个方法里面。我这里这个方法如下:
item = ArticleItem() soup =BeautifulSoup(response.body,"lxml") tag = soup.title item["title"] = tag.get_text() item["link"] = response.url if(len(soup.select(".otitle"))>0): item["summary"] = soup.select(".otitle")[0].get_text() else: item["summary"] = 'A' c='' for i in soup.select(".post_text > p"): c = c+i.get_text()#把每一段都拼接在一起 item["content"]=self.filterHtmlTag(c) a_list = soup.select("li > a") pattern = re.compile(r'(.+?)html$') url=[] url.append([i.get("href") for i in a_list if pattern.match(i.get("href"))]) print url yield item for src in url[0]: try: #yield Request(src,dont_filter=True,meta={'item':item},callback=self.parse_item) Request=scrapy.Request(src,callback=self.parse_item) yield Request log.msg('================================ %s has return request===================================' % src, log.WARNING) except Exception, e: log.msg('================================error %s===================================' % e, log.WARNING)
Scrapy 自带有解析的,我这里使用的是第三方的库 BeautifulSoup 在之前的文章里已经说过这个东西这段代码主要是解析获取的页面。如果只是要获取某一个页面直接解析就可以了,我这里是获取了页面之后如果还有新闻链接还要再进去抓取也就是如下这一段
for src in url[0]: log.msg('================================src %s===================================' % src, log.WARNING) try: #yield Request(src,dont_filter=True,meta={'item':item},callback=self.parse_item) Request=scrapy.Request(src,callback=self.parse_item) yield Request log.msg('================================ %s has return request===================================' % src, log.WARNING) except Exception, e: log.msg('================================error %s===================================' % e, log.WARNING)
这个时候基本已经可以抓取到文章了,默认会显示在终端抓取的东西,如果是要保存到数据库怎么做 在这里需要用到管道了 默认在目录下面有一个pipelines.py 这个文件 如果是想要保存到数据库或者其他什么地方可以在这里去操作 我这里是保存到mysql 我的操作类如下
#coding=utf-8 import MySQLdb import os, sys class mysql_util(): conn='' cursor = '' def __init__(self, host='xxx.mysql.rds.aliyuncs.com', user='train', password='Wxkj1234', db='train_main', charset='utf8'): try: self.conn = MySQLdb.connect(host, user, password, db) self.cursor = self.conn.cursor() dir(self.cursor) self.conn.set_character_set(charset) self.cursor.execute('SET CHARACTER SET utf8;') self.cursor.execute('SET character_set_connection=utf8;') except Exception, e: print e #查询数据 def query(self, sqlStr): result='' try: self.cursor.execute(sqlStr) result=self.getAllData() self.conn.commit() except: self.conn.rollback() return result #获取所有的数据 def getAllData(self): return self.cursor.fetchall() #获取单个数据 def getSingleData(self): return self.cursor.fetchone(); #更新或者插入 def executeSql(self,sql): try: self.cursor.execute(sql) self.conn.commit() except Exception, e: print e self.conn.rollback()
在TutorialPipeline里面主要是做一些持久化的功能,我这里在保存数据的同时 还将图片上传到OSS。代码如下
def process_item(self, item, spider): self.insert(item) return item def insert(self,item): u = mysql_util.mysql_util(); if(item["title"]==None or item["title"]== ''): pass sql_single = " select id,img_url from scrapy where title='{0}' limit 1".format(item["title"]) row = u.query(sql_single)if(row==None or len(row)==0): sql="insert into a_scrapy (scrapy_title,scrapy_content,scrapy_link,scrapy_create_on,scrapy_summary,scrapy_is_auth,scrapy_img_src,scrapy_create_timestamp) values('{0}','{1}','{2}',now(),'{3}',1,'{4}',unix_timestamp(now()))".format(item["title"],item["content"],item["link"],item["summary"],item["imageurl"]) #print sql u.executeSql(sql) else: id=row[0][0] img_url=row[0][1] if(item["imageurl"]!=''): if(img_url==''): sql_update = "update a_scrapy set scrapy_img_src='{0}' where scrapy_id={1}".format(item["imageurl"],id) u.executeSql(sql_update) else: sql_insert = "insert into a_scrapy_imgs (scrapy_id,img_url,is_auth,create_on) values({0},'{1}',1,now())".format(id,item["imageurl"]) print sql_insert u.executeSql(sql_insert)
上面就是将采集到的新闻存放到mysql里面,那么问题来了如果我要保存图片怎么办,这个时候要使用Scrapy自带的图片下载这块东西了,当然也可以自己去实现他,因为默认他是保存到本地,如果要保存到OSS或者其他CDN之类的还需要自己操作。新建一个DownloadImagesPipeline。主要方法如下:
def file_path(self,request,response=None,info=None): try: item=request.meta['item'] #通过上面的meta传递过来item index=request.meta['index'] #通过上面的index传递过来列表中当前下载图片的下标 log.msg('================================the imgae name %s ===================================' % item['imageurl'], log.WARNING) #得到图片后缀jpg,png ISOTIMEFORMAT='%Y-%m-%d' ext = 'jpg' if(request.url==None): ext='jpg' else: ext= request.url.split('/')[-1].split('.')[-1]
image_guid = str(random.randint(1,999))+str(time.time()).split('.')[0]+'.'+ ext log.msg('================================the image_guid %s ===================================' % image_guid, log.WARNING) filename = u'full/{0}/{1}'.format(time.strftime(ISOTIMEFORMAT, time.localtime() ), image_guid)if(len(item['imageurl'])>0): for url in item['imageurl']: if len(url)>10: r= requests.get(str(url)) if(r): auth = oss2.Auth('QRZM3PraYksFTHkA', 'E03FsWiAXuDSSYuqyGnOuqUK8UUSUO') bucket = oss2.Bucket(auth, 'oss-cn-hangzhou.aliyuncs.com', 'lvtudiandian') result = bucket.put_object("news_upload/{0}".format(filename),r) item['imageurl'] = "news_upload/{0}".format(filename) else: item['imageurl'] = '' return filename except Exception, e: log.msg(e)
我这里是直接上传的网络图片 也就是
filename = u'full/{0}/{1}'.format(time.strftime(ISOTIMEFORMAT, time.localtime() ), image_guid)if(len(item['imageurl'])>0): for url in item['imageurl']: if len(url)>10: r= requests.get(str(url)) if(r): auth = oss2.Auth('QRZM3PraYksFTHkA', 'E03FsWiAXuDSSYuqyGnOuqUK8UUSUO') bucket = oss2.Bucket(auth, 'oss-cn-hangzhou.aliyuncs.com', 'lvtudiandian') result = bucket.put_object("news_upload/{0}".format(filename),r) item['imageurl'] = "news_upload/{0}".format(filename)
原来采用的方法是在下载到本地之后再打开本地图片往上传,出现一个问题,很多时候图片已经存在可是就是找不到这个图片,所以后面改成了直接把图片链接拿过来取了文件流直接上传了。 到这里就结束了,这里只是作为一个例子如果需要用在生产环境中还需要去修改下代码,比如放到系统任务里自动去抓取这个。有什么问题或者建议可以在下面留言。