一.分析网页
目标网站:起点中文网
目标数据:类别(categoryName)小说书名(bookName) 小说链接(middleUrl)字数(wordsNums) 作者(updateTiems) 最新章节更新时间(authorName)
这里写图片描述
目标urls:”https://www.qidian.com/all?chanId=1&orderId=&style=2&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=1” 我们选取奇幻类的作测试 把page移动到最后方便后面的url拼接。
二.获取urls列表 1.取总页数 我们爬取多页的数据这时候就需要多个url了,根据上面的分析我们知道每一页page都不同所以我们只要改变url最后的”page=”就可以了 ,如果我们想获取这个系列所有的页数我们可以定位总页数。如图:
这里写图片描述 代码如下:
#总取页数 def getPages(self,url): htmlContent = self.getResponseContent(url) soup = BeautifulSoup(htmlContent, 'lxml') tags=soup.find('ul',attrs={'class':'lbf-pagination-item-list'}) #totalPages=tags.find_all('a')[-2].get('data-page') 取总页数 totalPages=1 #测试用 self.log.info(u'总页数为%s' %totalPages) return totalPages
2.根据总页数拼接url
# 根据pageSum拼装要爬取的地址 def getUrls(self,urlBase,pages): ulrs=[] ul=self.urlBase.split('=') for i in range(int(pages)+1): ul[-1]=str(i) url='='.join(ul) ulrs.append(url) self.log.debug('----待爬取的地址有:' + url) return ulrs
三.爬取目标数据 有了url我们就可以开始爬取我们想要的数据了!记得分析网页来进行元素定位。
#数据爬取 利用lxml解析 def spider(self,urls): for ulr in urls: htmlContent = self.getResponseContent(url) soup = BeautifulSoup(htmlContent, 'lxml') tableTag=soup.find_all('table',attrs={'class':'rank-table-list all'})[0] trTags=tableTag.tbody.find_all('tr') for tag in trTags: item=BookItem() tags=tag.find_all('td') item.categoryName=tags[0].getText().strip() item.bookName=tags[1].getText().strip() item.middleUrl=tags[1].find('a').get('href').strip() item.wordsNums=tags[3].getText().strip() item.authorName=tags[4].getText().strip() item.updateTiems=tags[5].getText().strip()
self.items.append(item) self.log.info(u'爬取%s 成功' %(item.categoryName))
四.保存数据到mysql 保存到text文本的方法这里就不再啰嗦。今天的重点是如何保存到mysql数据库。python提供了pymysql模块进行数据库相关操作 python 2—-> import MYSQLdb python 3—-> import pymysql 没有安装记得安装pymysql 记得提前在数据库中建表!!!!! 建表: 这里写图片描述
代码如下:
#! -*- encoding:utf-8 -*- """ 保存到mysql数据库
""" import pymysql pymysql.install_as_MySQLdb()
class SaveBooksDate(object): def __init__(self,items): self.host='localhost' self.port=3306 self.user='root' self.password='a' self.db='test' self.run(items)
def run(self,items): #创建连接 conn=pymysql.connect(host=self.host,port=self.port,user=self.user,password=self.password,db=self.db,charset='utf8') # 使用 cursor() 方法创建一个游标对象 cursor cur = conn.cursor() params=[] for item in items: params.append((item.categoryName, item.middleUrl, item.bookName, item.wordsNums, item.updateTiems,item.authorName))
sql="insert into qidianbooks(categoryName,middleUrl,bookName,wordsNums,updateTiems,authorName) values(%s, %s, %s, %s, %s, %s )" try: # 执行SQL ret=cur.executemany(sql,params) conn.commit() #提交 print(u'添加成功,共添加%s 条数据' %str(ret)) except Exception as e: print(e) conn.rollback() cur.close() # 关闭游标 conn.close() # 关闭连接
这里是新建的文件 记得在爬虫文件引入函数以及调用SaveBooksDate()函数 引入:
from save2mysql import SaveBooksDate
1
调用:
def __init__(self,url): self.urlBase=url self.log=MyLog() self.pages=self.getPages(self.urlBase) #根据urlBase取页数 self.items=[] self.urls=[] self.urls = self.getUrls(self.urlBase,self.pages) #根据页数获取url # 开始爬取 self.spider(self.urls) # 存 #self.pipelines(self.items) #存入mysql SaveBooksDate(self.items)
连接数据库的时候出了个bug: 这里写图片描述 后来才发现太粗心了少了一个括号: 这里写图片描述
以后一定要细心细心再细心!
四.结果及源代码
结果: 这里写图片描述
注:直接在mysql中读取可能乱码 这里我用navicat查看!
1.日志类 代码:
#! -*- encoding:utf-8 -*-\
""" 乱码问题 解决方式一:#! -*- encoding:utf-8 -*-\ 方式二:u'哈哈哈' 字符串以unicode格式存储
""" import logging import getpass import sys
class MyLog(object): #构造方法 def __init__(self): self.user=getpass.getuser() self.logger=logging.getLogger(self.user) self.logger.setLevel( logging.DEBUG ) #日志的级别 critical error warn info debug
#定义日志文件 self.logFile=sys.argv[0][0:-3]+'.log' # 从命令行参数中取出第一个参数,并取从0开始到 倒数第三个字符 拼接成文件名 self.formatter=logging.Formatter('%(asctime) -12s %(levelname) -8s %(name) -10s %(message)-12s\r\n') #日志输出的格式
#日志输出到文件 logging有三个内置的Handler, self.logHand=logging.FileHandler(self.logFile, encoding='utf8') self.logHand.setFormatter( self.formatter ) #设置 格式 self.logHand.setLevel( logging.DEBUG ) #设置 级别
#日志输出 到屏幕,这是标准输出流 self.logHandSt=logging.StreamHandler() self.logHandSt.setFormatter( self.formatter ) self.logHand.setLevel( logging.DEBUG )
#将两个Handler加入到 logger中 self.logger.addHandler( self.logHand ) self.logger.addHandler( self.logHandSt )
#重新定义logger中的日志输出的级别的方法 def debug(self,msg): self.logger.debug(msg)
def info(self,msg): self.logger.info(msg)
def warn(self,msg): self.logger.warn(msg)
def error(self,msg): self.logger.error(msg)
def critical(self,msg): self.logger.critical(msg)
if __name__=='__main__': mylog=MyLog() mylog.debug(u'debug测试') mylog.info(u'info测试') mylog.warn(u'warn测试') mylog.error(u'error测试') mylog.critical(u'critical测试')
2.爬虫类
代码:
#! -*- encoding:utf-8 -*-
#目标地址:https://www.qidian.com/all?chanId=1&orderId=&style=2&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=1 from MyLog import MyLog import string from urllib.parse import quote from urllib import error import urllib.request from bs4 import BeautifulSoup from save2mysql import SaveBooksDate import codecs import time import os
#pymysql class BookItem(object): categoryName=None middleUrl=None bookName=None wordsNums= None updateTiems= None authorName= None
class GetBook(object): def __init__(self,url): self.urlBase=url self.log=MyLog() self.pages=self.getPages(self.urlBase) #根据urlBase取页数 self.items=[] self.urls=[] self.urls = self.getUrls(self.urlBase,self.pages) #根据页数获取url # 开始爬取 self.spider(self.urls) # 存 #self.pipelines(self.items) SaveBooksDate(self.items)
# 根据pageSum拼装要爬取的地址 def getUrls(self,urlBase,pages): ulrs=[] ul=self.urlBase.split('=') for i in range(int(pages)+1): ul[-1]=str(i) url='='.join(ul) ulrs.append(url) self.log.debug('----待爬取的地址有:' + url) return ulrs #取页数 def getPages(self,url): htmlContent = self.getResponseContent(url) soup = BeautifulSoup(htmlContent, 'lxml') tags=soup.find('ul',attrs={'class':'lbf-pagination-item-list'}) #totalPages=tags.find_all('a')[-2].get('data-page') totalPages=1 self.log.info(u'总页数为%s' %totalPages) return totalPages
#数据爬取 利用lxml解析 def spider(self,urls): for ulr in urls: htmlContent = self.getResponseContent(url) soup = BeautifulSoup(htmlContent, 'lxml') tableTag=soup.find_all('table',attrs={'class':'rank-table-list all'})[0] trTags=tableTag.tbody.find_all('tr') for tag in trTags: item=BookItem() tags=tag.find_all('td') item.categoryName=tags[0].getText().strip() item.bookName=tags[1].getText().strip() item.middleUrl=tags[1].find('a').get('href').strip() item.wordsNums=tags[3].getText().strip() item.authorName=tags[4].getText().strip() item.updateTiems=tags[5].getText().strip()
self.items.append(item) self.log.info(u'爬取%s 成功' %(item.categoryName))
#读取URL指定的网页内容 def getResponseContent(self,url): #对地址的中文进行编码 try: url=quote(url,safe=string.printable) response=urllib.request.urlopen(url) except error.URLError as e: self.log.error(u'python爬取%s 出错了' %url) print (e) else: self.log.info(u'python爬取%s 成功' %url) return response.read()
#爬取的信息保存 def pipelines(self, items): filePrefixName =time.strftime('%Y%m%H%M%s',time.localtime()) fileName=os.path.expanduser("~")+os.path.sep+filePrefixName+u'.txt' with codecs.open(fileName, 'w', 'utf8') as fp: for item in items: fp.write('%s %s %s %s %s %s \n' % (item.categoryNam, item.bookName, item.middleUrl, item.wordsNums, item.authorName, item.updateTiems)) self.log.info(u'标题为 %s 的小说信息保存成功' % (item.categoryNam))
if __name__=='__main__': url=u'https://www.qidian.com/all?chanId=1&orderId=&style=2&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=1' tb=GetBook(url)
""" 保存到mysql库 python 2 import MYSQLdb python 3 import pymysql
"""
3.存数据库类
数据库代码如上—->四.保存数据到mysql 以后会写使用scrapy如何保存数据到mysql。大家也发现了仅仅一个分类就有几千页的数据使用爬虫爬取多页数据IP可能会被封掉以后我会结合实例演示如何反反爬虫。