1.selenium模块
(1)selenium模块简介
selenium是一个基于浏览器自动化的模块,可以便捷地获取动态加载的数据,便捷地实现模拟登录。
功能:
发起请求:get(url)
标签定位:find
标签交互:send_keys(‘xxx’)
执行js程序:excute_script(‘jscode’)
前进,后退:back(),forward()
关闭浏览器:quit()
(2)下载浏览器驱动(以chrome为例)
在chrome浏览器-帮助-关于google chrome中,可以看到chrome版本
在驱动下载链接 https://chromedriver.storage.googleapis.com/index.html中下载对应版本驱动
将exe程序放在如下目录下
定义一个selenium模块
from selenium import webdriver
bro=webdriver.Chrome(executable_path="../chromedriver/chromedriver")
(3)对于iframe的处理
以qq空间登陆界面为例,登录框是一个iframe子页面,使用selenium.find直接定位是定位不到的,所以需要先定位到iframe页面
#定位到iframe页面中
bro.switch_to.frame('login_frame')
(4)规避检测
一些网站会监测当前请求是否由selenium发出,需要规避检测
from selenium.webdriver import ChromeOptions
#实现规避检测
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
bro=webdriver.Chrome(executable_path="../chromedriver/chromedriver",options=option)
(5)案例
登录qq空间
在登录qq的情况下,点击头像就可登录
from selenium import webdriver
from time import sleep
#规避检测
from selenium.webdriver import ChromeOptions
#实现规避检测
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
bro=webdriver.Chrome(executable_path="../chromedriver/chromedriver",options=option)
bro.get('https://qzone.qq.com/')
#定位到iframe页面中
bro.switch_to.frame('login_frame')
sleep(2)
btn=bro.find_element_by_id('')#引号内填img_out_qq号
sleep(2)
btn.click()
sleep(10)
bro.quit()
成功登录
登录12306(过滑块)
分别定位到用户名、密码和登录:
点击“登录”,会弹出滑块,定位到滑块元素,使用动作链进行滑动。
滑动完成后成功登录。
代码:
from selenium import webdriver
from time import sleep
from selenium.webdriver import ChromeOptions
#定义动作链
from selenium.webdriver import ActionChains
#实现规避检测
option=ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
bro=webdriver.Chrome(executable_path='../chromedriver/chromedriver',options=option)
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
sleep(2.1)
username=bro.find_element_by_id('J-userName')
username.send_keys('')#引号内填账户名
sleep(1.2)
password=bro.find_element_by_id('J-password')
password.send_keys('')#引号内填密码
sleep(3.1)
login=bro.find_element_by_id('J-login')
login.click()
# 设置没有webdriver属性,避免被检测
script = 'Object.defineProperty(navigator,"webdriver",{get:()=>undefined,});'
bro.execute_script(script)
sleep(3.2)
slider=bro.find_element_by_id('nc_1_n1z')
action=ActionChains(bro)
action.click_and_hold(slider)
action.move_by_offset(300,0).perform()
action.release()
二、scrapy框架
1.模块安装
pip install wheel
pip install pywin32
pip install scrapy
2.在终端输入指令创建一个工程,假设工程名命名为firstBlood
scrapy startproject firstBlood
创建工程后目录如下
3.进入firstBlood目录,在终端输入指令在spiders中创建一个爬虫文件,假设文件命名为first
cd firstBlood
scrapy genspider first www.xxx.com
创建爬虫文件后目录如下
4.持久化存储数据
(1)基于终端
局限性:只可将parse文件的返回值存储到本地的文本文件中,存储的文件类型只能为json、jsonlines、jl、csv、xml等;
指令:scrapy crawl xxx -o filepath
好处:简洁高效
【案例】爬取百度热搜标题存储到csv文件
first.py
import scrapy
import sys
sys.path.append(r'D:\pycharm_project\reptileStudy\code\scrapyProject\firstBlood\firstBlood')
import items
class FirstSpider(scrapy.Spider):
#爬虫文件的名称,爬虫源文件的唯一标识
name = 'first'
#允许的域名,用来限定start_urls哪些可以进行请求发送
#allowed_domains = ['http://scxk.nmpa.gov.cn:81/xk/']
#起始的url列表,该列表中的url会被scrapy自动进行请求发送
start_urls = ['http://www.baidu.com']
#基于终端的持久化存储,只能存为固定的几种文件格式
#用作数据解析,response参数是请求成功后对应的相应对象
def parse(self, response):
li_list=response.xpath('//ul[@class="s-hotsearch-content"]/li')
#li_list=response.xpath('//ul[@id="gzlist"]/li[1]/dl/a/text()')
print(li_list)
all_data=[]
for li in li_list:
title=li.xpath('.//span[@class="title-content-title"]/text()')[0].extract()
print(title)
dic={
"title":title
}
all_data.append(dic)
return all_data
settings.py
BOT_NAME = 'firstBlood'
SPIDER_MODULES = ['firstBlood.spiders']
NEWSPIDER_MODULE = 'firstBlood.spiders'
# UA伪装
USER_AGENT = "'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'"
# Obey robots.txt rules,君子协议
ROBOTSTXT_OBEY = False
#只显示错误类型的信息
LOG_LEVEL='ERROR'
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'firstBlood.pipelines.FirstbloodPipeline': 300,#300表示的是优先级,数值越小优先级越高
}
在终端输入scrapy crawl first -o ../first.csv
爬取成功
(2)基于管道
编码流程:
数据解析,在item中定义相关的属性,把解析的数据存储到item类型的对象,把item类型的对象提交给管道的process_item函数进行持久化存储,在配置文件中开启管道。
好处:通用性强
【案例】将爬取的百度热搜题目存储到数据库
first.py
import scrapy
import sys
sys.path.append(r'D:\pycharm_project\reptileStudy\code\scrapyProject\firstBlood\firstBlood')
import items
class FirstSpider(scrapy.Spider):
#爬虫文件的名称,爬虫源文件的唯一标识
name = 'first'
#允许的域名,用来限定start_urls哪些可以进行请求发送
#allowed_domains = ['http://scxk.nmpa.gov.cn:81/xk/']
#起始的url列表,该列表中的url会被scrapy自动进行请求发送
start_urls = ['http://www.baidu.com']
#基于管道的持久化存储
def parse(self, response):
li_list=response.xpath('//ul[@class="s-hotsearch-content"]/li')
#li_list=response.xpath('//ul[@id="gzlist"]/li[1]/dl/a/text()')
print(li_list)
all_data=[]
count=0
for li in li_list:
count = count + 1
title=li.xpath('.//span[@class="title-content-title"]/text()')[0].extract()
print(count)
print(title)
item=items.FirstbloodItem()
item['title']=title
item['count']=count
yield item
items.py
import scrapy
class FirstbloodItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
count = scrapy.Field()
pass
pipelines.py
import pymysql
class FirstbloodPipeline:
fp=None
#重写父类的一个方法,该方法只在开始爬虫时被调用一次
def open_spider(self,spider):
print('开始爬虫---')
self.fp=open('../first.txt','w',encoding='utf-8')
#接收爬虫文件提交过来的item对象
#该方法每接收到一个item就会被调用一次
def process_item(self, item, spider):
title=item['title']
count=item['count']
self.fp.write(title+'\n')
return item #传递给下一个即将被执行的管道类
def close_spider(self,spider):
print('结束爬虫---')
self.fp.close()
#管道文件中一个管道类对应将一组数据存储到一个平台或载体中
class mysqlPipeLine(object):
conn=None
cursor=None
def open_spider(self,spider):
self.conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='123456',db='sakila')
def process_item(self,item,spider):
self.cursor = self.conn.cursor()
try:
self.cursor.execute(f'insert into baidutitle values("{item["count"]}","{item["title"]}")')
self.conn.commit()
except Exception as e:
print(e)
self.conn.rollback()
return item
def close_spider(self,spider):
self.cursor.close()
self.conn.close()
settings.py
BOT_NAME = 'firstBlood'
SPIDER_MODULES = ['firstBlood.spiders']
NEWSPIDER_MODULE = 'firstBlood.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = "'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36'"
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
#只显示错误类型的信息
LOG_LEVEL='ERROR'
ITEM_PIPELINES = {
'firstBlood.pipelines.FirstbloodPipeline': 300,#300表示的是优先级,数值越小优先级越高
'firstBlood.pipelines.mysqlPipeLine': 301
}
在终端输入scrapy crawl first
查看数据库
成功爬取。
5.管道存储图片(scrapy.pipelines.images)
4是用来存储文本,若存储图片,可以用scrapy.pipelines.images。
步骤:
1.对图片地址进行数据解析;
2.把item提交到管道;
3.在管道中定制一个基于scrapy.pipelines.images的管道类;
4.在配置文件中开启管道类,设置为自定义的管道;设置图片存储地址。
【案例】爬取学习通PPT
如图所示,学习通课程内嵌的PPT无法整个下载,它是由一张张图片组成,若想保存需要逐张另存,工作量很大。我们可以通过请求图片链接进行批量下载。
步骤
(1)定位到PPT所在子页面,可以看到它属于一个iframe,这个iframe有一个链接。
但链接只是一部分,我们需要在这个链接前拼接https://mooc1-2.chaoxing.com/
,才是完整链接。
(2)进入拼接后的网址,如上图。发现这里面PPT还是在一个子页面中,再次检查这一页的iframe地址。
可以看到这个iframe中的src是完整的,直接访问这个src所对应的网页。
(3)访问后,该网页中看到的就是一张张图片,每张图片对应一个链接,爬虫对这些链接发送request请求,则可以下载。
代码
结构目录
second.py
import scrapy
import sys
sys.path.append(r'D:\pycharm_project\reptileStudy\code\scrapyProject\secondBlood\secondBlood')
import items
class SecondSpider(scrapy.Spider):
name = 'second'
allowed_domains = ['www.xxx.com']
start_urls = ['https://pan-yz.chaoxing.com/screen/file_5807583ce09b9083dbb4809301792d4b?ext=%7B%22_from_%22%3A%22221688283_48606782_57041418_6ac69e101569e5992d151553e49dd379%22%7D']
def parse(self, response):
img_list=response.xpath("/html/body/div[1]/img")
count=1
for img in img_list:
item = items.SecondbloodItem()
img_url=img.xpath('./@src')[0].extract()
item['img']=img_url
item['name']=str(count)+'.png'
count=count+1
yield item
pipelines.py
from scrapy.pipelines.images import ImagesPipeline
import scrapy
class imgsPipeLine(ImagesPipeline):
#请求图片
def get_media_requests(self,item,info):
yield scrapy.Request(item['img'])
#指定存储路径
def file_path(self, request, response=None, info=None, *, item=None):
imgname=item['name']
return imgname
#把item返回给下一个被执行的管道类
def item_completed(self, results, item, info):
return item
settings.py
BOT_NAME = 'secondBlood'
SPIDER_MODULES = ['secondBlood.spiders']
NEWSPIDER_MODULE = 'secondBlood.spiders'
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = "User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36"
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
LOG_LEVEL = 'ERROR'
IMAGES_STORE='../images'
ITEM_PIPELINES = {
'secondBlood.pipelines.imgsPipeLine': 300, #要执行的管道类,自定义类后一定要更换
}
items.py
import scrapy
class SecondbloodItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
img=scrapy.Field()
name=scrapy.Field()
pass
在pycharm终端的scrapyProject\secondBlood路径下,输入scrapy crawl second
执行
图片已存在images文件夹下: