scrapy+selenium爬取需要登录需要验证码的网站
站在巨人的肩膀上
采取了大神的思路,加上自己项目的需求
本文只是将结果打印了出来,没有保存过程,后续会继续写文章
思路注释上都有,就不墨迹了
import json
import time
import urllib
import urllib.request
from pathlib import Path
import scrapy
from scrapy.http import Request,FormRequest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
class DbSpider(scrapy.Spider):
name = 'long'
allowed_domains = ['dc.oilchem.net']
# 模拟请求的headers,非常重要,不设置也可能网站不让你访问
headers = {
'Host': 'dc.oilchem.net',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36',
}
# start_urls = ['http://douban.com/']
# scrapy请求的开始时start_request
def start_requests(self):
longzhong_findUrl='https://dc.oilchem.net/price_search/list.htm?businessType=2&varietiesName=PP%E7%B2%92&varietiesId=319&templateType=6&flagAndTemplate=3-4;2-7;6-null;1-6;6-null&channelId=1775&oneName=%E5%A1%91%E6%96%99&twoName=%E9%80%9A%E7%94%A8%E5%A1%91%E6%96%99'
if not Path('longzhongCookies.json').exists():
__class__.loginlongzhong()
# 先执行login,保存cookies之后便可以免登陆操作
#毕竟每次执行都要登陆还是挺麻烦的,我们要充分利用cookies的作用
#从文件中获取保存的cookies
with open('longzhongCookies.json','r',encoding='utf8') as f:
listcookies=json.loads(f.read()) # 获取cookies
#把获取的cookies处理成dict类型
cookies_dict=dict()
for cookie in listcookies:
# 在保存成dict时,我们其实只要cookies中name和value,而domain等其他都可以不要
cookies_dict[cookie['name']]=cookie['value']
# scrapy发起其他页面请求时,带上cookies=cookies_dict即可,同时记得带上header值,
yield scrapy.Request(url=longzhong_findUrl,cookies=cookies_dict,callback=self.parse,headers=__class__.headers)
#使用selenium登陆隆重并获取登陆后的cookies,后续需要登录的操作都可以利用cookies
@staticmethod
def loginlongzhong():
#登录网址
loginurl='https://www.oilchem.net'
#加载webdriver驱动,用于获取登陆页面标签属性
driver=webdriver.Chrome()
#加载页面
driver.get(loginurl)
time.sleep(3) #执行休眠3s等待浏览器的加载
# 方式1:通过填充用户名和密码
# 获取隐藏的登陆页面
try:
driver.find_element_by_partial_link_text(u"登录").click()
print("测试通过,partial_link_text定位成功")
except Exception as e:
print("测试失败,未定位到partial_link_text", format(e))
# 获取用户名框
driver.find_element_by_name('username').clear()
# 填充用户名
driver.find_element_by_name('username').send_keys(u'15021328563')
# 获取密码框
driver.find_element_by_name('password').clear()
# 填充密码
driver.find_element_by_name('password').send_keys(u'15021328563')
input('检查网页是否有验证码要输入,有就在网页输入验证码,输入完,控制台按回车;如果无验证码,则直接回车:')
# 点击登录按钮,有时候网页会在输入密码后弹出验证码,这一步之后人工校验
# < button type = "button" onclick = "dialogLogin()" > 登录 < / button >
# <div class="form-sub"><button type="button" οnclick="dialogLogin()">登录</button></div>
driver.find_element_by_class_name("form-sub").click()
# # 方式2 直接通过扫描二维码,如果不是要求全自动化,建议用这个,非常直接,毕竟我们这一步只是想保存登录后的cookies,至于用何种方式登录,可以不必过于计较
# driver.find_element_by_css_selector(
# "button[class='Button Button--plain']").click()
# input("请扫描页面二维码,并确认登录后,点击回车:") # 点击二维码手机扫描登录
time.sleep(3) #同样休眠3s等待登录完成
input("检查网页是否有完成登录跳转,如果完成则直接回车:")
# 通过上述的方式实现登录后,其实我们的cookies在浏览器中已经有了,我们要做的就是获取
# selenium为我们提供了get_cookies来获取登陆cookies的作用
cookies=driver.get_cookies()
driver.close() #获取cookies便可以关闭浏览器
# 然后关键就是保存cookies,之后请求从文件中读取cookies就可以省去每次都要登录一次的
# 当然可以吧cookies返回回去。但是之后每次请求都要先执行一次login没有发挥cookies的作用
jsonCookies=json.dumps(cookies) #通过json将cookies写入文件
with open('longzhongCookies.json','w') as f:
f.write(jsonCookies)
print(cookies)
def parse(self, response):
diqu = response.xpath("//div[@class='containerList line-height22']/div[@class='container']/div")
print('解析文本:-----------------')
# print(diqu)
for table in diqu:
# table.xpath(...) 提取的一个列表,其中只有一个值 加上[0],取列表中的第一个
title = table.xpath(".//div[@class='container-title']/div/span/text()")[0]
# title是一个Selector选择器 用选择器.extract()提取其中的data数据
# extract(): 这个方法返回的是一个数组list,,里面包含了多个string,如果只有一个string,则返回['ABC']
# 这样的形式。
# extract_first():这个方法返回的是一个string字符串,是list数组里面的第一个字符串。
print(title.extract())
# print(table)
# table.xpath(...) 提取的一个列表,其中只有一个值 加上[0],取列表中的第一个
tableShow = table.xpath(".//div[@class='tabCar']/div[@class='tableList shows']")[0]
# print(tableShow)
table_header = tableShow.xpath(".//table//tr/th/text()").extract()
td1_product_list = tableShow.xpath(".//table//tr/td[1]/a/text()").extract()
td2_SPEC_list = tableShow.xpath(".//table//tr/td[2]/text()").extract()
td3_norm_list = tableShow.xpath(".//table//tr/td[3]/text()").extract()
# <td>销售公司名</td><td></td>
# 这个字段存在<td></td>这样的形式,如果还用/text()的形式,只能提取到['销售公司名'],如果用.xpath('string(.)')可以提取到['销售公司名','']
# 好处是 长度一致。 具体问题具体分析嘛
td4_sellcompany_html = tableShow.xpath(".//table//tr/td[4]")
td4_sellcompany_list = []
for i in td4_sellcompany_html:
td4_sellcompany_list.append(i.xpath('string(.)').extract_first())
td5_productcompany_list = tableShow.xpath(".//table//tr/td[5]/text()").extract()
td10_price_list = tableShow.xpath(".//table//tr/td[10]/@price").extract()
# print(td_list)
print('{}有:{}条数据'.format(title.extract(), len(td10_price_list)))
t_header = [table_header[0], table_header[1], table_header[2],
table_header[3], table_header[4], table_header[9]]
print(t_header)
for t1, t2, t3, t4, t5, t10 in zip(td1_product_list, td2_SPEC_list, td3_norm_list,
td4_sellcompany_list, td5_productcompany_list, td10_price_list):
list_per = [t1, t2, t3, t4, t5, t10]
print(list_per)