爬虫04
1.爬虫介绍
- python是做爬虫比较方便,很多爬虫的库。其次java、go
- http协议
- pc端、小程序、app
- 模拟发送http请求,拿到返回数据然后解析出我们想要的数据,最后保存气起来。
- requests,selenium bs4
2.request模块使用
- 发送get请求
- 请求地址中带数据:直接拼、params参数
- 编码问题、url编码
- 携带请求头:user-agent、referer
- 携带cookie:登录后cookie、就是登录状态
- 带在请求头中
- 带在cookie参数中:字典、cookiejar对象
- 发送post请求
- 携带数据:data、json、传文件类型
- Response对象的属性和方法
- text
- headers
- cookies
- content:文件
3.代理
- 封ip、代理
- 代理ip:收费
- 开源的代理池
4.requests爬取视频
- referer,视频地址还需要转换
5.bs4解析xml库
- html是xml的一种
6.bs4遍历
- .
- 取属性
- 取文本:text、string、strings
- 嵌套使用
- 父亲、兄弟,子孙...
7.bs4搜索
- 可以和遍历连用
- find、find_all
- 5种搜索方式:字符串、布尔、正则、列表、方法
8.css选择器
- 标签名
- 类名
- id
9.selenium
- 控制浏览器、浏览器驱动跟浏览器版本对应
- python操作浏览器
- 无头浏览器
- 等待
- 搜索方式:
find_element(by='', value='')
- css选择器:CSS_SELECTOR
- xpath
- 获取cookie
- 将cookie保存到本地
- 打开页面,把cookie写入
10.抽屉半自动点赞
- 使用requests发送点赞请求,需要携带cookie
- 先使用selenium登录进去,然后拿到cookie
- 最后用requests使用自动点赞。
一、xpath的使用
-
html中选择标签可以使用的通用方式有2种:css选择器和xpath选择。
XPath即XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言
-
语法的简单介绍
nodename 选取此节点的所有子节点 / 从根节点选取 /body/div // 从匹配选择当前节点,而不考虑它们的位置 //div . 选取当前节点 .. 选取当前节点的父节点 @ 选取属性
-
最简单的方式copy复制
doc = '''
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html' id='id_a'>Name: My image 1 <br/><img src='image1_thumb.jpg' /></a>
<a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html' class='li li-item' name='items'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
<a href='image6.html' name='items'><span><h5>test</h5></span>Name: My image 6 <br /><img src='image6_thumb.jpg' /></a>
</div>
</body>
</html>
'''
from lxml import etree
html = etree.HTML(doc)
# html = etree.parse('search.html', etree.HTMLParser())
# 1.所有节点
a = html.xpath('//*')
# 2.指定节点(结果为列表)
b = html.xpath('//head')
# 3.子节点,子孙节点
c = html.xpath('//div/a')
d = html.xpath('//body/a') # 子节点没有a则没有数据
e = html.xpath('//body//a')
# 4.父节点
aa = html.xpath('//body//a[@href="image1.html"]/..')
bb = html.xpath('//body//a[1]/..')
# 也可以这样
cc = html.xpath('//body//a[1]/parent::*')
dd = html.xpath('//body//a[1]/parent::div')
# 5.属性匹配
ee = html.xpath('//body//a[@href="image1.html"]')
# 6.文本获取 text()
ff = html.xpath('//body//a[@href="image1.html"]/text()')
# 7.属性获取 @
g = html.xpath('//body//a/@href')
gg = html.xpath('//body//a/@id')
# 8.属性多值匹配
# a标签有多个class类,直接匹配就不行,需要yogacontains
h = html.xpath('//body//a[@class="li"]')
hh = html.xpath('//body//a[@name="items"]')
i = html.xpath('//body//a[contains(@class,"li")]')
ii = html.xpath('//body//a[contains(@class, "li")]/text()')
# 多属性匹配
j = html.xpath('//body//a[contains(@class, "li")or @name="items"]')
jj = html.xpath('//body//a[contains(@class, "li") and @name="items"]/text()')
#10.按序选择
k = html.xpath('//a[2]/text()')
kk = html.xpath('//a[3]/@href')
# 取最后一个
l = html.xpath('//a[last()]/@href')
# 位置小于3
ll = html.xpath('//a[position() < 3]/@href')
# 倒数第二个
m = html.xpath('//a[last()-2]/@href')
# 节点轴选择
# 祖先节点:ancestor
# 使用了* 获取所有祖先节点
mm = html.xpath('//a/ancestor::*')
# 获取祖先节点中的div
n = html.xpath('//a/ancestor::div')
# 属性值:attribute
nn = html.xpath('//a[1]/attribute::*')
o = html.xpath('//a[1]/attribute::href')
# 直接子节点:child
oo = html.xpath('//a[1]/child::*')
# 所有子孙节点:descendant
p = html.xpath('//a[6]/descendant::*')
#当前节点之后所有节点
pp = html.xpath('//a[1]/following::*')
# q = html.xpath('//a[1]following::*[1]/@href')
# 当前节点之后同级节点:following-sibing
qq = html.xpath('//a[1]/following-sibling::*')
s = html.xpath('//a[1]/following-sibling::a')
ss = html.xpath('//a[1]/following-sibling::*[2]')
sss = html.xpath('//a[1]/following-sibling::*[2]/@href')
print(b)
二、selenium动作链
-
网站中有些按住鼠标,滑动的效果===》滑动验证码
-
两种形式
-
形式一:
actions = ActionChains(bro) # 拿到动作链对象 actions.drag_and_drop(sourse, target) # 把动作放到动作链中,准备串行执行 actions.perform()
-
形式二:
ActionChains(bro).click_and_hold(sourse).perform() distance = target.location['xxx']-sourse.location['xx'] track = 0 while track < distance: ActionChains(bro).move_by_offset(xoffset=2, yoffset=0).perform() track +=2
import target as target from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver import ActionChains import time bro = webdriver.Chrome(executable_path='./chromedriver.exe') bro.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable') bro.implicitly_wait(10) try: bro.switch_to.frame('iframeResult') ## 切换到iframeResult sourse = bro.find_element(by=By.ID, value='draggable') target -= bro.find_element(by=By.ID, value='droppable') # 方式一:基于同一个动作链串行执行 # actions = ActionChains(bro) # 拿到动作链对象 # actions.drag_and_drop(sourse, target) # 把动作放在动作链中,准备串行执行 # actions.perform() # 方式二:不同的动作链,每次移动的位移都不同 ActionChains(bro).click_and_hold(sourse).perform() distance = target.location['x'] - sourse.location['x'] print('目标距离源的x轴距离:', distance) track = 0 while track < distance: ActionChains(bro).move_by_offset(xoffset=2, yoffset=0).perform() track += 2 ActionChains(bro).release().perform() time.sleep(10) except Exception as e: print(e) finally: bro.close()
-
三、自动登录12306
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled") # 去掉自动化控制的提示
bro = webdriver.Chrome(executable_path='./chromedriver.exe', options=options)
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
bro.maximize_window()
# 12306检测到我们使用了selenium控制了浏览器,所以它的滑块出不来
bro.implicitly_wait(10)
try:
username = bro.find_element(by=By.ID, value='J-userName')
username.send_keys('13735760305')
password = bro.find_element(by=By.ID, value='J-password')
password.send_keys('xcc123')
time.sleep(3)
btn = bro.find_element(by=By.ID, value='J-login')
btn.click()
span = bro.find_element(by=By.ID, value='nc_1_n1z')
ActionChains(bro).click_and_hold(span).perform() # 鼠标点主
ActionChains(bro).move_by_offset(xoffset=300, yoffset=0).perform() # 滑动
time.sleep(10)
except Exception as e:
print(e)
finally:
bro.close()
注意:
* 有的网站可以检测到使用了自动化控制所以我们需要加一行代码:options.add_argument("--disable-blink-features=AutomationControlled")
四、打码平台使用
- 使用打码平台自动登录``
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from chaojiying import ChaojiyingClient
from PIL import Image
bro = webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('http://www.chaojiying.com/apiuser/login/')
bro.implicitly_wait(10)
bro.maximize_window()
try:
username = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[1]/input')
password = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[2]/input')
code = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[3]/input')
btn = bro.find_element(by=By.XPATH, value='/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input')
username.send_keys('306334678')
password.send_keys('lqz123')
# 获取验证码:
#1 整个页面截图
bro.save_screenshot('main.png')
# 2 使用pillow,从整个页面中截取出验证码图片 code.png
img = bro.find_element(By.XPATH, '/html/body/div[3]/div/div[3]/div[1]/form/div/img')
location = img.location
size = img.size
print(location)
print(size)
# 使用pillow扣除大图中的验证码
img_tu = (int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
# # 抠出验证码
# #打开
img = Image.open('./main.png')
# 抠图
fram = img.crop(img_tu)
# 截出来的小图
fram.save('code.png')
# 3 使用超级鹰破解
chaojiying = ChaojiyingClient('306334678', 'lqz123', '937234') # 用户中心>>软件ID 生成一个替换 96001
im = open('code.png', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
print(chaojiying.PostPic(im, 1902)) # 1902 验证码类型 官方网站>>价格体系 3.4+版 print 后要加()
res_code=chaojiying.PostPic(im, 1902)['pic_str']
code.send_keys(res_code)
time.sleep(5)
btn.click()
time.sleep(10)
except Exception as e:
print(e)
finally:
bro.close()
五、使用selenium爬取京东商品信息
from selenium import webdriver
from selenium.webdriver.common.by import By # 按照什么方式查找,By.ID,By.CSS_SELECTOR
import time
from selenium.webdriver.common.keys import Keys
def get_goods(driver):
try:
goods = driver.find_elements(by=By.CLASS_NAME, value='gl-item')
for good in goods:
name = good.find_element(by=By.CSS_SELECTOR, value='.p-name em').text
price = good.find_element(by=By.CSS_SELECTOR, value='.p-price i').text
commit = good.find_element(by=By.CSS_SELECTOR, value='.p-commit a').text
url = good.find_element(by=By.CSS_SELECTOR, value='.p-name a').get_attribute('href')
img = good.find_element(by=By.CSS_SELECTOR, value='.p-img img').get_attribute('src')
if not img:
img ='https://'+ good.find_element(by=By.CSS_SELECTOR, value='.p-img img').get_attribute('data-lazy-img')
print('''
商品名字:%s
商品价格:%s
商品链接:%s
商品图片:%s
商品评论:%s
''' % (name, price, url, img, commit))
button = driver.find_element(by=By.PARTIAL_LINK_TEXT, value='下一页')
button.click()
time.sleep(1)
get_goods(driver)
except Exception as e:
print(e)
def spider(url, keyword):
driver = webdriver.Chrome(executable_path='./chromedriver.exe')
driver.get(url)
driver.implicitly_wait(10) # 使用隐式等待
try:
input_tag = driver.find_element(by=By.ID, value='key')
input_tag.send_keys(keyword)
input_tag.send_keys(Keys.ENTER)
get_goods(driver)
finally:
driver.close()
if __name__ == '__main__':
spider('https://www.jd.com/', keyword='精品内衣')
六、scrapy介绍
-
前面学的都是模块如果是做专业的爬虫需要使用框架,类似于
django:web
-
scrapy:爬虫框架
- 做爬虫用的东西都封装好了,只需要在固定的位置上写固定的代码即可
-
scrapy:号称爬虫界的django
- django 大而全,做web相关的它都用到
- scrapy 大而全,做爬虫相关的它都用到
-
介绍:
Scrapy一个开源和协作的框架,起初为了页面的抓取(更准确来说,网络抓取)所设计的,使用它可以快速、简单、可扩展的方式从网站中提取所需的数据。但是目前Scrapy的用途十分广泛,可用于数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据或通用的网络爬虫
-
安装 scrapy
* 针对mac、linux: pip3 install scrapy * 针对win:可能会出错(看情况) pip3 install scrapy 如果安装失败可能操作系统缺少一些依赖。 1.pip3 install wheel # 安装后,方便通过wheel文件安装软件 xx.whl 2.pip3 insatll lxml 3.pip3 install pyopenssl 4.下载并安装pywin32: https://sourceforge.net/projects/pywin32/files/pywin32/ 5.下载twisted的wheel文件: http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted 6.执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl 7.pip3 install scrapy
-
释放出scrapy 可执行文件
- 以后使用这个创建爬虫项目===》django-admin创建django项目
-
创建爬虫项目
scrapy startproject myfirstscrapy
-
创建爬虫【django创建app】
scrapy genspider 项目名(爬虫名字) 爬取的地址
-
启动爬虫
scrapy crawl cnblogs --nolog
-
pycharm中运行
新建run.py from scrapy.cmdline import execute execute(['scrapy', 'crawl', 'cnblogs','--nolog'])
七、scrapy架构介绍
-
引擎(EGINE)
引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。有关详细信息,请参照上面的数据流部分。
-
调度器(SCHEDULER)
用来接受引擎发过来的请求并压入队列中,并在引擎再次请求的时候返回,可以想象成一个URL的优先级队列,由它决定下一个要抓取的网址是什么,同时去除重复的网址
-
下载器(DOWLOADER)
用于下载网页内容并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的
-
爬虫(SPIDERS)
SPIDERS是开发人员自定义的类,用来解析responses,并且提取items或者发送新的请求
-
项目管道(ITEM PIPLINES)
在item被提取后负责处理他们,主要包括清理、验证、持久化(比如存到数据库)等操作
-
下载器中间件(DOWLOADER Middlewares)
位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应responses,你可以用该中间件做一下几件事情: 1.设置请求, 2.设置cookie 3.使用代理 4.集成selenium
-
爬虫中间件(Spider Middlewares)
位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)
八、scrapy解析数据
-
response对象有css方法和xpath方法
- css中写css选择器
- xpath中写xpath选择
-
重点1
* xpath取文本内容 './/a[contains(@class,"link-title")]/text()' * xpath取属性 './/a[contains(@class,"link-title")]/@href' * css取文本 'a.link-title::text' * css取属性 'img.image-scale::attr(src)'
-
重点2
.extract_first() 取一个 .extract() 取所有
import scrapy
from bs4 import BeautifulSoup
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
allowed_domains = ['www.cnblogs.com']
start_urls = ['http://www.cnblogs.com/']
def parse(self, response):
# response类似于requests模块的response对象
# print(response.text)
# 返回的数据,解析数据
方式1:使用bs4(不用了)
# 方式一:使用bs4
soup = BeautifulSoup(response.text, 'lxml')
article_list = soup.find_all(class_='post-item')
for article in article_list:
title_name = article.find(name='a', class_='post-item-title').text
print(title_name)
方式2:scrapy自带的解析> css解析
article_list = response.css('article.post-item')
for article in article_list:
title_name = article.css('section>div>a::text').extract_first()
author_img = article.css('p.post-item-summary>a>img::attr(src)').extract_first()
desc_list = article.css('p.post-item-summary::text').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.css('section>footer>a>span::text').extract_first()
article_date = article.css('section>footer>span>span::text').extract_first()
# 文章详情内容,因为在下一页,先不考虑
print('''
文章标题:%s
作者标题:%s
摘要:%s
作者名字:%s
发布日期:%s
''' % (title_name, author_img, desc, author_name, article_date))
方式3:scrapy自带的解析> xpath解析
article_list = response.xpath('//article[contains(@class,"post-item")]')
for article in article_list:
title_name = article.xpath('./section/div/a/text()').extract_first()
author_img = article.xpath('./section/div/p//img/@src').extract_first()
desc_list = article.xpath('./section/div/p/text()').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.xpath('./section/footer/a/span/text()').extract_first()
article_date = article.xpath('./section/footer/span/span/text()').extract_first()
print('''
文章标题:%s
作者标题:%s
摘要:%s
作者名字:%s
发布日期:%s
''' % (title_name, author_img, desc, author_name, article_date))
九、settings相关配置,提高爬取效率
基础配置
# 爬虫项目名
BOT_NAME = 'myfirstscrapy'
# 指定爬虫类的py文件的位置
SPIDER_MODULES = ['myfirstscrapy.spiders']
NEWSPIDER_MODULE = 'myfirstscrapy.spiders'
# 用户类型(携带在请求头里)
USER_AGENT = 'myfirstscrapy (+http://www.yourdomain.com)'
# Obey robots.txt rules==>是否遵循爬虫协议
ROBOTSTXT_OBEY = True
# LOG_LEVEL 日志级别
LOG_LEVEL = 'ERROR' # 报错如果不打印日志,在控制台看不到错误
# 启动线程数(多少个爬虫同时工作)
CONCURRENT_REQUESTS = 32
# Override the default request headers:==>默认请求头
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
# 爬虫中间件
SPIDER_MIDDLEWARES = {
'myfirstscrapy.middlewares.MyfirstscrapySpiderMiddleware': 543,
}
# 下载中间件
DOWNLOADER_MIDDLEWARES = {
'myfirstscrapy.middlewares.MyfirstscrapyDownloaderMiddleware': 543,
}
# 持久化配置
ITEM_PIPELINES = {
'myfirstscrapy.pipelines.MyfirstscrapyPipeline': 300,
}
增加爬虫的爬取效率
1.增加并发 默认开启线程数为16
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改
CONCURRENT_REQUESTS = 100
值为100相当于并发设置成了100.
2.降低日志级别
在运行scrapy时会有大量日志信息的输出。为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在settings配置文件写:
LOG_LEVEL = 'INFO'
3.禁止cookie
如果不是真的需要cookie,则在scrapy爬取数据时候禁止cookie从而减少CPU的使用率,提高爬取效率。在配置文件中编写:
COOKIES_ENABLED = False
4.禁止重试
对失败的HTTP进行重新请求(重试)会减少爬取速度,因此可以禁止重试。在配置文件中编写:
RETRY_ENABLED = False
5.减少下载超时
如果对用一个非常慢的链接进行爬取,减少下载超时可以让卡住的链接快速被放弃,从而提高效率。在配置文件中降序编写:
DOWNLOAD_TIMEOUT = 10s
十、 持久化方案
-
保存到硬盘上
-
两种方案:第二种常用
-
第一种:了解
* 解析函数中parse,需要return [{}, {}, {}] # 列表套字典 * scrapy crawl cnblogs -o 文件名(json, pickle, csv结尾的文件)
-
第二种:使用
pipeline
常用的管道形式,可以同时存多个位置的。1. 在items.py 中写一个类[相当于django的表模型],继承scrapy.Item 2. 在类中写属性,写字段,所有字段都是scrapy.Field类型 eg: title = scrapy.Field() 3. 在爬虫中导入类,实例化得到对象,把要保存的数据放到对象中 item['title'] = title 【注意:不能用 . 的形式去获取】 解析类中 yield item 4. 修改配置文件,指定pipline,数字表示优先级,越小则优先级越大 ITEM_PIPELINES = { 'crawl_cnblogs.pipelines.CrawlCnblogsPipeline': 300, } 5. 写一个pipline:CrawlCnblogsPipeline * open_spider:数据初始化,打开文件,打开数据库链接 * process_item:真正存储的地方 注意: 最后需要return item,交给后续pipeline继续使用 * close——spider:销毁资源,关闭文件,关闭数据库链接
-
十一、全站爬取cnblogs文章
- 第一页爬完后,要保存的数据已经保存
- 之后需要欧两件事情:
- 继续爬取下一页数据===>解析洗衣液的地址,包装成request对象
- 继续爬取详情页===>解析出详情页的地址,包装成request对象
- 现在在这里不能保存,因为数据不全,缺少文章详情,我们需要加上文章详情后再一次性保存。
request和response对象传递参数
-
Request创建
-
在parse中,for循环中,创建Request对象时,传入meta
yield Request(url=url, callback=self.detail_parse,meta={'item':item})
-
-
Response对象
-
detail_parse中,通过response取出meta再取出meta里面的item,把文章详情写入。
yield item
-
解析下一页并继续爬取
spiders/cnblogs.py
from scrapy import Request
from myfirstscrapy.items import CnblogsItem
import scrapy
from bs4 import BeautifulSoup
class CnblogsSpider(scrapy.Spider):
name = 'cnblogs'
allowed_domains = ['www.cnblogs.com']
start_urls = ['http://www.cnblogs.com/']
def parse(self, response):
# item = CnblogsItem() # 外面定义,会有问题
article_list = response.xpath('//article[contains(@class,"post-item")]')
for article in article_list:
item = CnblogsItem() # 定义在for内部,每次都是一个新对象
title_name = article.xpath('./section/div/a/text()').extract_first()
author_img = article.xpath('./section/div/p//img/@src').extract_first()
desc_list = article.xpath('./section/div/p/text()').extract()
desc = desc_list[0].replace('\n', '').replace(' ', '')
if not desc:
desc = desc_list[1].replace('\n', '').replace(' ', '')
author_name = article.xpath('./section/footer/a/span/text()').extract_first()
article_date = article.xpath('./section/footer/span/span/text()').extract_first()
url = article.xpath('./section/div/a/@href').extract_first()
item['title_name'] = title_name
item['author_img'] = author_img
item['desc'] = desc
item['author_name'] = author_name
item['article_date'] = article_date
item['url'] = url
# print(url)
# 现在不存了,因为数据不全,等全了以后再存,继续爬取,就要创建Request对象
# 详情页面,使用self.detail_parse解析
yield Request(url=url, callback=self.detail_parse, meta={'item': item})
# 解析出下一页地址
# css
next_url = 'https://www.cnblogs.com' + response.css('div.pager>a:last-child::attr(href)').extract_first()
print(next_url)
yield Request(url=next_url, callback=self.parse)
def detail_parse(self, response):
# print(len(response.text))
item = response.meta.get('item')
# 解析详情
article_content = response.css('div.post').extract_first()
# print(article_content)
# print('===================')
# 把详情,写入当前meta中得item中
item['article_content'] = str(article_content)
yield item
items.py
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class CnblogsItem(scrapy.Item):
# # define the fields for your item here like:
# # name = scrapy.Field()
# pass
title_name = scrapy.Field()
author_img = scrapy.Field()
desc = scrapy.Field()
author_name = scrapy.Field()
article_date = scrapy.Field()
url = scrapy.Field()
article_content = scrapy.Field() # 文章详情
pipelines.py
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# useful for handling different item types with a single interface
import pymysql
from itemadapter import ItemAdapter
class CnblogsMysqlPipeline:
def open_spider(self, spider):
# 打开数据库链接
self.conn = pymysql.connect(
user='root',
password='123',
host='127.0.0.1',
database='cnblogs',
port=3306,
autocommit=True # 自动提交,配置后就不用在下面添加 self.conn.commit()
)
self.cursor = self.conn.cursor()
def process_item(self, item, spider):
self.cursor.execute(
'insert into artice (title_name,author_img,`desc`,author_name,article_date,url,article_content) values (%s,%s,%s,%s,%s,%s,%s)',
args=[item['title_name'], item['author_img'], item['desc'], item['author_name'], item['article_date'],
item['url'], item['article_content']])
# self.conn.commit() # 提交
return item
# 关闭数据库
def close_spider(self, spider):
self.cursor.close()
self.conn.close()
class CnblogsFilesPipeline:
def open_spider(self, spider):
# cnblogs这个爬虫对象
self.f = open('cnblogs.txt', 'at', encoding='utf-8')
def process_item(self, item, spider):
# 存真正的数据,每次item都会走一次
print(item)
# 存文件不用这种方式,最好爬虫启动,把文件打开爬虫结束文件就关闭了
with open('cnblogs.txt', 'at', encoding='utf-8') as f:
f.write('文章标题:%s, 文章作者:%s\n' % (item['title_name'], item['author_name']))
# return item
self.f.write('文章标题:%s, 文章作者:%s\n' % (item['title_name'], item['author_name']))
return item
def close_spider(self, spider):
print('我关了')
self.f.close()
十二、爬虫和下载中间件
-
scrapy的所有中间件都写在middlewares.py中,跟django非常像,做一些拦截。
-
爬虫中间件(用的很少,了解即可)
MyfirstscrapySpiderMiddleware def process_spider_input(self, response, spider): # 进入爬虫会执行它 def process_spider_output(self, response, result, spider): #从爬虫出来会执行它 def process_spider_exception(self, response, exception, spider):#出了异常会执行 def process_start_requests(self, start_requests, spider):#第一次爬取执行 def spider_opened(self, spider): #爬虫开启执行 # 下载中间件 MyfirstscrapyDownloaderMiddleware def process_request(self, request, spider): # request对象从引擎进入到下载器会执行 def process_response(self, request, response, spider):# response对象从下载器进入到引擎会执行 def process_exception(self, request, exception, spider):#出异常执行它 def spider_opened(self, spider): #爬虫开启执行它
重点:process_request, process_response
下载中间件的process_request
* 返回值 * return None:继续执行下面的中间件的process_request * return a Response object:不进行下载中间件,直接返回给引擎,引擎把它给爬虫 * return a Request object:不进入下载中间件,直接返回给引擎,引擎把它放到调度器中。 * raise IgnoreRequest:process_exception() 抛异常,会执行process_exception
下载中间件的process_response
* 返回值 * return a Response object:正常,会进入到引擎,引擎会把它给爬虫 * return a Request object:会进入到引擎,引擎会把它放进调度器中, 等待下次被爬 * raise IgnoreRequest 会执行process_exception