为了家庭,14年辞职从杭州xxx回到湖南老家一个事业单位工做。平时工做量很少,闲暇时间本身捣腾捣腾技术。python一直听闻使用起来比较简单,上手起来也比较快,刚好朋友手头有个项目,项目具体要求以下:
php
1、客户有个APP,APP整天发一些和他们业务相关的新闻,新闻源头都是来自于各大相关网站,可是因为客户本身平时时间紧凑,没有不少闲暇时间去搜集各大网站他们想要的文章,因而就有了这个需求。客户提供20多个网站网址,天天定时从各大网站抽取他们APP想要的文章。
html
2、抽取后的文章到APP后台作一个审核功能,审核经过就直接转载到他们APP。
python
3、文章须要归类,不一样文章类型最好在后台归类归好,也方便他们发布和挑选消息。
web
4、文章须要去重以避免影响其挑选速度
数据库
以前工做时就看到同事用python工具爬虫去爬取别人网站信息,因而就有了本身也学一下python的兴趣。入门步骤开始,各类大牛绕道,别喷我这菜B了。windows
1、python安装,直接进入到官网 https://www.python.org/downloads/ 点击download你要的版本,我是下载了3.5最新版本。浏览器
2、要抓取别人网站内容,我在网上搜了一下解析html工具,使用 BeautifulSoup ,http://www.crummy.com/software/BeautifulSoup/bs4/download/ 这里能够下载,windows下也能够直接下载tar.gz文件。网络
3、安装python 略多线程
4、安装BeautifulSoup 模块,用3.5.0就遇到这个坑,在py setup.py build / py setup.py install 以后 在命令行 执行 from bs4 import BeautifulSoup 会报错,看提示说是 叫我从新安装或者 run 2to3 将 bs4从2版本代码转换到3里面也兼容,python在这方面作的比较好。因而我在python安装目录中找到 py ~/tools/scripts/2to3.py -w setup.py app
在安装完成以后,必定要关闭掉命令窗口从新打开,要否则死也不会提示成功。
5、能够编码了,打开python编辑器,固然,在抓取别人网站前提先要了解一下这个网站页面的结构,我是要抓取热点新闻,因此从首页下手,发现首页热点dom里面有惟一标记 div的class =centertopnew 和 class=newslist ,因而我能够直接从html中直接把这两个节点提取出来,而后提取出带超连接的标签<a href=xxxx>xxx</a>,把文章名称和文章超连接提取出来保存到本地文件中(刚入门,图简单直接写到了文件,后面确定会移到数据库中)
#coding=utf-8 import urllib.request import logging import urllib.parse import time from bs4 import BeautifulSoup ##日志文件 LOG_PATH = "E:/pythonwork/spider/log/spider_log.log" ## 获取页面以后编码转换 PAGE_CODE = "GBK" ##爬虫每爬一个页面在该目录下写一个文件 FILE_BASE_PATH = "E:/pythonwork/spider/files/" ## 模拟火狐浏览器user-agent U_A = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" HEADERS = {"User-Agent" : U_A } REQ_URL = "http://news.carnoc.com" ##初始化日志类 logger=logging.getLogger() logger.setLevel(logging.INFO) handler=logging.FileHandler(LOG_PATH) logger.addHandler(handler) console = logging.StreamHandler() console.setLevel(logging.DEBUG) # 设置console日志打印格式 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) values={'wd':'python', 'opt-webpage':'on', 'ie':'gbk'} url_values=urllib.parse.urlencode(values) url_values=url_values.encode(encoding='gb2312') ##构建request类 def getRequestClass(url,url_values): return urllib.request.Request(url,url_values,HEADERS) ##获取网页内容函数 def getHtml(req_url): try: response=urllib.request.urlopen(req_url) except urllib.HTTPError as e: print('Error code:',e.code) except URLError as e: print('Reason',e.reason) return response.read().decode("GBK") ##写文件函数 def createNewFileAndWrite(file_name,content): file_obj = open(FILE_BASE_PATH+file_name+".txt","a") file_obj.write(content) file_obj.close() ##createNewFileAndWrite("test","中文测试内容") ##根据dom提取文章title和文章超连接 def getTitleAndUrl(newsHTML): resultList = [] for root in newsHTML: tempa = root.find_all("a") for dom in tempa: kv = [] href = dom.get("href") title = dom.string kv.append(title) kv.append(href) resultList.append(kv) return resultList ##循环dom list写入文件中 def writeList(domList,partName): TIME_FORMAT = "%Y-%m-%d " now = time.strftime(TIME_FORMAT,time.localtime()) for dom in domList: createNewFileAndWrite(partName+"-"+now,dom[0]+"\1"+dom[1] + "\n") ## 入口函数 def start(): logging.warn("start get html content ...") htmlContent = getHtml(getRequestClass(REQ_URL,url_values)) logging.warn("get html success ...") soup = BeautifulSoup(htmlContent,"html.parser") logging.warn("write content to txt ...") ##热点新闻 class=centertopnew writeList(getTitleAndUrl(soup.find_all("div",class_="centertopnew")),"centertopnew") ##各类新闻 class=newslist writeList(getTitleAndUrl(soup.find_all("div",class_="newslist")),"newslist") #入口 start()
上面文件保存,本地直接运行就会去网站里面提取首页的热点文章,而后页面里每一块新闻右上角都有“更多”,固然,这是我最想要的,由于在更多里面我能够提取更多我想要的文章。
6、根据首页抓取文章,直接进一步抓取文章内容,而且作深度递归抓取,我暂时设置深度为3,因而有了第二个代码:
#coding=utf-8 ## 进入耳机页面进行疯狂的抓取,抓取深度为3 ## 配置url规则 import re import urllib.request import logging import urllib.parse import time import traceback import os from bs4 import BeautifulSoup DEPTH = 3 ##日志文件 LOG_PATH = "E:/pythonwork/spider/log/spider_log.log" ## 获取页面以后编码转换 PAGE_CODE = "GBK" ##爬虫每爬一个页面在该目录下写一个文件 FILE_BASE_PATH = "E:/pythonwork/spider/files/" ## 模拟火狐浏览器user-agent U_A = "Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)" HEADERS = {"User-Agent" : U_A } ##初始化日志类 logger=logging.getLogger() logger.setLevel(logging.INFO) handler=logging.FileHandler(LOG_PATH) logger.addHandler(handler) console = logging.StreamHandler() console.setLevel(logging.DEBUG) # 设置console日志打印格式 formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') console.setFormatter(formatter) logging.getLogger('').addHandler(console) linkHashMap = {} values={'wd':'python', 'opt-webpage':'on', 'ie':'gbk'} url_values=urllib.parse.urlencode(values) url_values=url_values.encode(encoding='gb2312') reg = "^http://news.carnoc.com/{1}[]$" ##读取抓取首页后的文章 def getIndexResult(filepath): file_obj = open(filepath) try: all_content = file_obj.read() return all_content except Exception as ex: print(ex) finally: file_obj.close() p = re.compile(r"http://news.carnoc.com/(cache/)?(list/){1}(\w(/\w)*)+.html") ##深度挖掘数据,暂定为深度为3 def iteratorDrillData(content,depth): print("*******************当前深度:【"+str(depth)+"】*********************") if depth > 0 : contentlist = content.split("\n") for con in contentlist: kv = con.split("\1") try: if len(kv)>1 and kv[1] : if "http://news.carnoc.com/" not in kv[1]: kv[1] = "http://news.carnoc.com/"+kv[1] logging.info("请求"+kv[1]) if kv[1] in linkHashMap: break htmlContent = getHtml(getRequestClass(kv[1],url_values)) if htmlContent : alist = getTitleAndUrl(htmlContent) newsBlock = getPageNewsSoup(htmlContent) ## print(newsBlock) if newsBlock : fromSite = newsBlock.find_all(id="source_baidu") publishDate = newsBlock.find_all(id="pubtime_baidu") author = newsBlock.find_all(id="author_baidu") ##print(newsBlock[0]) newsText = newsBlock.find(id="newstext") if newsText : createNewFileAndWrite(mkdirBefore(kv[1][23:len(kv[1])]),str(newsText)) ##print(alist) for m in alist: if m and m[1] and p.match(m[1]): try: print(m) iteratorDrillData(m[0]+"\1"+m[1],depth-1) except: continue except : traceback.print_exc() continue def mkdirBefore(urlString): s = urlString.split("/") # lis = s[0:len(s)-1] # for i in range(0,len(s)-1): # if not os.path.exists(FILE_BASE_PATH+lis[i]): #os.mkdir(FILE_BASE_PATH+lis[i]) #print("建立目录"+lis[i]) print("写入目录文件:"+"html/"+s[-1]) return "html/"+s[-1] def getRequestClass(url,url_values): linkHashMap[url] = 2 return urllib.request.Request(url,url_values,HEADERS) ##获取网页内容函数 def getHtml(req_url): try: response=urllib.request.urlopen(req_url) except : return None return response.read().decode("GBK") ##写文件函数 def createNewFileAndWrite(file_name,content): file_obj = open(FILE_BASE_PATH+file_name+".txt","w+") file_obj.write(content) file_obj.close() ##根据dom提取文章title和文章超连接 def getTitleAndUrl(newsHTML): resultList = [] root = BeautifulSoup(newsHTML,"html.parser") tempa = root.find_all("a") for dom in tempa: kv = [] href = dom.get("href") title = dom.string kv.append(title) kv.append(href) resultList.append(kv) return resultList def getPageNewsSoup(htmlContent): return BeautifulSoup(htmlContent,"html.parser") ##http://news.carnoc.com/(cache/)?(list/){1}(\w)*(/\w)*.html iteratorDrillData(getIndexResult("E:/pythonwork/spider/files/centertopnew-2015-09-18 .txt"),3)
若是您想直接使用我代码,注意要修改代码中的文件目录,先把目录建好,须要在目录里面新建 log 和html目录,log存放python日志,html存放爬过来的文章内容。当初为了快速上手目的,没有把一切作成全自动的,由于要花一些时间来研究python的函数。
7、上面代码意思是把第一个代码匹配出来的连接请求进去,而后在请求返回结果中提取文章内容(也可能页面没有文章,多是“更多”,点进去就是文章列表)抽取文章内容也是根据dom的id来提取的。
8、在代码中能够看到我使用了递归,递归深度为3,意思是把当前连接请求进去,而后把返回结果中有超链的连接抠出来继续请求,相似于深度优先,其实我的以为,在抓取网站时,广度优先要好一点,由于在跑数据的时候,目录一层一层的生成会看得比较清晰当前深度爬到了什么程度!
因为我本地网络缘由,请求常常超时,爬了2小时才爬了几百条数据,固然,我这种效率低是理所固然,后期确定会使用多线程甚至多进程。
因为输入数据和输出数据均可以很相似 “文章标题”,“文章内容”,“文章超连接”,因此同一份代码能够继续拿上一次跑出来的结果继续递归来跑,这种相似广度优先,先把当前页面全部符合要求的超连接所有请求一边,结果所有存起来,而后把所有结果归总以后继续用程序又所有跑一边,又得出结果...如此以来能够直接递归下去(若是文章数不多,而且页面上没有展现出各个文章之间的粘性,这种方式很那把文章爬完),我这种递归要求页面和页面之间有必定的关系,好比该页面有些推荐了另一个页面,这样就会找到下一个页面了。
8、去重
当前代码去重是直接把url当作key ,每爬一个就放到字典里面去,在爬一个url以前判断一下我有没有爬过,爬过就算了,写文件这里其实也至关于有去重 open('xxx','w') 会直接覆盖掉重名的文件名。我这种去重只适合与单个网站去重,若是把20多个网站数据爬到一块儿,那么有可能会有问题,好比多个网站使用了phpwind,他们网站url规律都查不到,重复概率较高,不能用这个方式去重,要否则会覆盖掉不该该覆盖的数据。
9、文章归类,还得去研究研究,经过文本内容自动归类若是有作过这种东西的朋友能够在评论里面告知我,我也好学学!
文章写的很差,见谅