爬取目标:顶点小说网
pip install scrapy
scrapy startproject xxx xxx项目名字
Scrapy默认是不能在IDE中调试的,我们在根目录中新建一个py文件叫:entrypoint.py;在里面写入以下内容:
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'scrapy_demo'])
注意!第二行中代码中的前两个参数是不变的,第三个参数请使用自己的spider的名字。
import scrapy
class ScrapyDemoItem(scrapy.Item):
# define the fields for your item here like:
# 小说名字
name = scrapy.Field()
# 小说作者
author = scrapy.Field()
# 文章类别
category = scrapy.Field()
Item对象是种简单的容器,保存爬取到的数据。提供了类似词典的API及简单的语法
在spiders文件中新建一个scrapy_demo.py文件
倒入需要用的模块
import re
import scrapy
from bs4 import BeautifulSoup
from scrapy.http import Request # 一个单独的Request模块,需要跟进url的时候用它
from scrapy_demo.items import ScrapyDemoItem #导入在items中定义的字段
在倒入ScrapyDemoItem类的时候遇到了,倒入失败的情况。
解决方法:
目录的某个包中的某个py文件要调用另一个py文件中的函数,首先要将目录 设置为source root,这样才能从包中至上至上正确引入函数。
步骤:目录 > 右键 > make directory as > source root
分析地址:http://www.23us.so/list/1_1.html
格式为:http://www.23us.so/list/x_y.html
其中x表示小说的分类,y代表页码。
class MySpider(scrapy.Spider):
name = 'scrapy_demo'
allowed_domains = ['www.23us.so']
bash_url = 'http://www.23us.so/list/'
bashurl = '.html'
"""爬取的文章类型"""
def start_requests(self):
for i in range(1,2):
url = self.bash_url + str(i) + '_1' + self.bashurl
yield Request(url, self.parse)
def parse(self, response):
print(response.text)
def parse(self, response):
soup = BeautifulSoup(response.text, 'lxml')
# max_num = soup.find(id = 'pagelink').find_all(name='a')[-1].text
max_num = 4
bashurl = str(response.url)[:-7]
for num in range(1, int(max_num) + 1): # 拼接完整的url
url = bashurl + '_' + str(num) + self.bashurl
yield Request(url, callback=self.get_name)
获取地址:http://www.23us.so/list/1_1.html
"""获取小说的大概信息"""
def get_name(self, response):
novellist = BeautifulSoup(response.text, 'lxml').find_all('tr', bgcolor='#FFFFFF')
for novel in novellist:
novelname = novel.find('a').text # 获取小说的名字
novelurl = novel.find('a')['href'] # 小说的url
yield Request(novelurl, callback=self.get_chapterurl, meta={'name': novelname, 'url': novelurl}) # meta传递额外参数
"""获取小说的具体信息"""
def get_chapterurl(self, response):
item = ScrapyDemoItem() # 将倒入的item文件实例化
item['name'] = str(response.meta['name']) # 获取上个函数返回的小说名字和url
item['novelurl'] = response.meta['url']
soup = BeautifulSoup(response.text, 'lxml') # 获取小说url地址的内容
category = soup.find('table').find('a').text # 获取小说的类型
author = soup.find('table').find_all('td')[1].text.lstrip() # 获取小说的作者
bash_url = soup.find('p', class_='btnlinks').find('a', class_='read')['href'] # 最新章节的连接
name_id = bash_url.split('/')[-2] # 小说的id来判断在数据库的存取
item['category'] = category
item['author'] = author
item['name_id'] = name_id
return item
DROP TABLE IF EXISTS `dd_name`;
CREATE TABLE `dd_name` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`xs_name` varchar(255) DEFAULT NULL,
`xs_author` varchar(255) DEFAULT NULL,
`category` varchar(255) DEFAULT NULL,
`name_id` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=38 DEFAULT CHARSET=utf8mb4;
MYSQL_HOSTS = '127.0.0.1'
MYSQL_USER = 'root'
MYSQL_PASSWORD = 'qwer1234'
MYSQL_PORT = 3306
MYSQL_DB = 'python'
使用的是pymysql
下载命令:pip install pymysql
import pymysql
from scrapy_demo import settings
MYSQL_HOSTS = settings.MYSQL_HOSTS
MYSQL_USER = settings.MYSQL_USER
MYSQL_PASSWORD = settings.MYSQL_PASSWORD
MYSQL_PORT = settings.MYSQL_PORT
MYSQL_DB = settings.MYSQL_DB
# 打开数据库连接
db = pymysql.connect(host=MYSQL_HOSTS, user=MYSQL_USER, password=MYSQL_PASSWORD, db=MYSQL_DB, port=MYSQL_PORT, use_unicode=True, charset="utf8")
# 使用cursor()方法获取操作游标
cur = db.cursor()
class Sql:
@classmethod
def insert_dd_name(cls, xs_name, xs_author, category, name_id):
sql = 'insert into dd_name (xs_name, xs_author, category, name_id) values (%(xs_name)s, %(xs_author)s, %(category)s, %(name_id)s)'
value = {
'xs_name' : xs_name,
'xs_author' : xs_author,
'category' : category,
'name_id' : name_id
}
cur.execute(sql, value)
db.commit()
"""会查找name_id这个字段,如果存在则会返回 1 不存在则会返回0"""
@classmethod
def select_name(cls, name_id):
sql = "select exists(select 1 from dd_name where name_id = %(name_id)s)"
value = {
'name_id' : name_id
}
cur.execute(sql, value)
return cur.fetchall()[0]
from .sql import Sql
from scrapy_demo.items import ScrapyDemoItem
from scrapy_demo.items import DcontentItem
class ScrapyDemoPipeline(object):
def process_item(self, item, spider):
if isinstance(item, ScrapyDemoItem):
name_id = item['name_id']
ret = Sql.select_name(name_id)
if ret[0] == 1:
print('已经存在小说')
else:
xs_name = item['name']
xs_author = item['author']
category = item['category']
Sql.insert_dd_name(xs_name, xs_author, category, name_id)
print('开始存取小说')
ITEM_PIPELINES = {
# 'scrapy_demo.pipelines.ScrapyDemoPipeline': 300,
'scrapy_demo.mysqlpipelines.pipelines.ScrapyDemoPipeline': 1,
}
PS: scrapy_demo(项目目录).mysqlpipelines(自己建立的MySQL目录).pipelines(自己建立的pipelines文件).DingdianPipeline(其中定义的类) 后面的 1 是优先级程度(1-1000随意设置,数值越低,组件的优先级越高)
运行的是entrypoint.py文件
url = 'http://www.23us.so/xiaoshuo/18747.html'
# response = requests.get(url).content.decode('utf-8')
response = requests.get(url).text
print(response)
获取的是乱码,其实是ASCII格式只能显示英文不能显示中文。
text返回的unicode类型的数据。
content返回的是bytes,二进制类型。
所以在获取页面信息的时候,将content直接解码为utf-8
response = requests.get(url).content.decode('utf-8')
2.在获取存取小说内容的时候 地址
内容有回车和制表符号,获取的内容是短短续续的。
**提示**:常用空格是\x20标准ASCII可见字符 0x20~0x7e 范围内,而 \xa0 属于 latin1 (ISO/IEC_8859-1)中的扩展字符集字符,代表空白符nbsp(non-breaking space)。 latin1 字符集向下兼容 ASCII ( 0x20~0x7e )。
使用方法来去除掉制表符和回车
s = ''.jion(s.split())
join(): 连接字符串数组。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串。
split():split方法中不带参数时,表示分割所有换行符、制表符、空格。
3.在数据库存储数据遇到问题:
python3 pymysql 'latin-1' codec can't encode character 错误 问题解决
# 打开数据库连接
db = pymysql.connect("localhost","root","00000000","TESTDB" ,use_unicode=True, charset="utf8")
原因:pymysql 正常情况下会尝试将所有的内容转为latin1字符集处理,latin1格式的表结构,不可以存放中文,因为是单字节编码,但是向下兼容ASCII。