一、确定基本框架
1.确定基本分类
首先我所要编写的程序虽然简单,但因需要多次对数据库进行添加和查询,所以我将整个程序分为了两个py类:一个novelsql类用于编写对数据库操作的代码块;
其余的对网页的爬取和分析在一个novel类中。按说应该把爬取和分析分成两个类,但本程序代码简单并且不多,所以就没有分开了。
2.确定基本功能
该程序在我的想法中应具有以下的基本功能:
1.提供大的小说分类目录供选择
2.提供查询多页和特定页中的小说列表
3.提供下载特定小说的全部免费章节,并对所下载的内容进行简单的分类和调整下载文本格式(因为从网下爬下来的小说章节没有进行分页且有的语句过长,从而在记事本中查看需要使用者自己去横向的拉会很麻烦。),同时保存小说的封面图片。
基本上就这些功能,看着是不是很简单,很轻松,是不是觉得我很low?对我就是很low,你能怎么办。反正我就写了这些功能(- -!)
二 、代码编写
1.数据库操作
因为两个类中对数据库的操作要简单,所以我们先来编写对数据库的操作。首先我要明白这个类中的代码基本是供另一个类来调用的,所以我们在编写代码是应写在函数中。在py文件中导入另一个py类时,如果代码写没有写在函数中,那在加载你的py类时就会直接先将这些代码进行运行
首先我们需要导入mysql的控制
import mysql.connector
我们需要对数据库作的操作有创建表,添加,查询。
创建表的代码
def createtable(tablename,i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
sql1="DROP TABLE IF EXISTS "+tablename
sql="CREATE TABLE IF NOT EXISTS "+tablename+"(Id INT auto_increment ,num INT,t_Type VARCHAR(255),NovelName VARCHAR(25),Url VARCHAR(255), PRIMARY KEY(Id,num,NovelName))"
mycursor.execute(sql1)
mycursor.execute(sql)
mycursor.close()
mydb.close()
其中的mysql.connector.connect(host,user,passwd,database)
是对数据库创建链接,里面有着四个参数。host表示你所链接数据库的地址(这里我连接的是本地服务器,所以之是localhost);user是你访问数据库的用户名(我用的是默认用户名root);passwd是你访问数据库的密码(我没有所以用的空);database是你所访问的数据库名称。
mycursor=mydb.cursor()
这是在获取数据库游标,方便之后对sql语句的进行操作。在对sql语句进行重组时我用的是字符串拼接,这样存在一个很严重的问题——sql注入攻击。我想过取用占位符来占位但python好像不支持,如果有各位有什么好的方法请一定私信我。
向表中添加数据的代码为:
def adddata(tablename,t_type,novelname,url,i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
sql1="SELECT NovelName FROM "+tablename+" WHERE NovelName='"+novelname+"'"
mycursor.execute(sql1)
re=mycursor.fetchall()
if(re == []):
sql="INSERT INTO "+tablename+"(num,t_Type,NovelName,Url)VALUES ("+"1"+",'"+t_type+"','"+novelname+"','"+url+"') "
mycursor.execute(sql)
mydb.commit()
mycursor.close()
mydb.close()
查询的部分分两种,一种为对数据库查询表名。代码如下:
def sqlquery(i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
mycursor.execute("show tables")
return mycursor.fetchall()
另一种是对表中的数据按需查询。
def query(tablename,name,i,condition,n):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(n))
mycursor=mydb.cursor()
if i==0:
sql="SELECT "+name+" FROM "+tablename
else:
sql="SELECT "+name+" FROM "+tablename+" WHERE NovelName='"+condition+"'"
mycursor.execute(sql)
return mycursor.fetchall()
数据库的整体代码:
import mysql.connector
def database(i):
database=("feilu","feilu2")
return database[i]
def createtable(tablename,i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
sql1="DROP TABLE IF EXISTS "+tablename
sql="CREATE TABLE IF NOT EXISTS "+tablename+"(Id INT auto_increment ,num INT,t_Type VARCHAR(255),NovelName VARCHAR(25),Url VARCHAR(255), PRIMARY KEY(Id,num,NovelName))"
mycursor.execute(sql1)
mycursor.execute(sql)
mycursor.close()
mydb.close()
def adddata(tablename,t_type,novelname,url,i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
sql1="SELECT NovelName FROM "+tablename+" WHERE NovelName='"+novelname+"'"
mycursor.execute(sql1)
re=mycursor.fetchall()
if(re == []):
sql="INSERT INTO "+tablename+"(num,t_Type,NovelName,Url)VALUES ("+"1"+",'"+t_type+"','"+novelname+"','"+url+"') "
mycursor.execute(sql)
mydb.commit()
mycursor.close()
mydb.close()
def query(tablename,name,i,condition,n):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(n))
mycursor=mydb.cursor()
if i==0:
sql="SELECT "+name+" FROM "+tablename
else:
sql="SELECT "+name+" FROM "+tablename+" WHERE NovelName='"+condition+"'"
mycursor.execute(sql)
return mycursor.fetchall()
def sqlquery(i):
mydb=mysql.connector.connect(
host = "localhost",
user = "root",
passwd = "",
database=database(i))
mycursor=mydb.cursor()
mycursor.execute("show tables")
return mycursor.fetchall()
可能会有小伙伴说我的代码怎么还有一个数据库的选择。这是因为我对两个库进行了分别的处理。要说为什么不用一个…我懒啊,用一个时会存在一些不方便。
2.网页爬取
首先是页面代码需要导入的模块有
import requests
import bs4
import urllib.request
import os
import stat
import chardet
import re
import novelsql
import lxml.html
import webbrowser
导入后我们先来看看飞卢的主页。
首先选择我们要爬取的内容,在此我用箭头标注的就是我本次程序所爬的内容。之所以不爬完,是因为太耗时间而且其他列表的爬取是一样的,所以想添加的小伙伴可以自行添加。在这里我是讲爬取的内容存到数据库,应为当时没有提前想好所以在储存时有点失误,我在项目中是直接创建以列表名为表名的表,然后再增加数据,但这样在现在看来有问题,不应该分如此多的表而是应该在表中去创建属性来区分,这样我也不需要用两个数据库。(没事我会在寒假去完善的。)
那么来获取的代码和结果如图:
因为我基本是将代码进行了封装。所以我解释一下函数的意思,具体代码后面一次会给出。第一个是根据URL去获取网页的数据。第二个是根据soup去将导航中选项与其所对应的网址存入数据库。(注意:飞卢中的有的URL并不是一个具体的网址,在存入时要进行判断,不是的要在前面加入**https://**不然无法进行访问和获取数据。)第三个是将列表中的数据,以列表名来分别存入小说名和地址,有的列表还需存入月票数与点击数。第四个是去获取刚刚存入的表名,用于向控制台输出。(因为我没写界面,所以就把控制台当做了界面进行操作。)
这个方法的完整代码。
def getmysql():
url_first = "https://b.faloo.com/"
soup = getSoup(url_first)
getbook_list(soup)
getNovels_list(soup)
reslust=novelsql.sqlquery(0)
return reslust
def getSoup(url):
htmlText = getHtmlText(url)
soup = bs4.BeautifulSoup(htmlText,'html.parser')
return soup
def getHtmlText(url):
r=requests.get(url,timeout=30)
r.raise_for_status()
r.encoding="gbk"
return r.text
def getbook_list(soup):
data =soup.find("div",{"class":"i_mm"})
tablename=data.find("div",{"id":"tab2_box1_1"}).text
novelsql.createtable(tablename,0)
datas = data.find("div",{"id":"con_tab2_box_1"}).find("div",{"class":"c_list0"}).find_all("a")
n=0
for re in datas:
if n ==