爬虫初级学习

注意:由于本篇文章的代码写了有一段时间了,所以仅供参考,不保证都能测试成功

第一章:request基础

安装requests库:

pip install requests 

接下来,介绍requests的一些基础用法:

案例1.1.1get请求获取百度搜索结果

需求:

1.使用get方法,模拟百度请求

2.获得响应的数据

技术:requestsget请求、text

 

复制代码
这里主要介绍的是有参数的get请求

import requests   #引入requests库
query = input("请输入一个你喜欢的明星")  #将我们要搜索的内容用一个变量去接收

url = f"https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=88093251_22_hao_pg&wd={query}"  #这是我们要爬取的地址,其中?后面的都为参数,而wd后面的内容就是我们输入的内容

dic = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
}  #在请求头中找到的'User-Agent',可以将我们的代码伪装成浏览器去请求

#发送get请求
resp = requests.get(url,headers=dic)  #使用headers处理反爬

print(resp.text)  #将响应的内容以文本的形式显示
print(resp.url)  #返回请求的url
复制代码

 

案例1.1.2:post请求获取百度翻译结果

这里主要介绍的是有参数的post请求

需求:

1.使用post方法,模拟百度请求

2.获得响应的数据

技术:requestspost请求、json格式

 

复制代码
import requests
url = "https://fanyi.baidu.com/sug"

s = input("输入要翻译的单词")
dat = {
    'kw': s
}  #我们需要把请求的数据给浏览器发过去,所以这里需要准备一个字典来发送数据,kw为参数名,s为我们想要翻译的内容

#发送post请求,发送的数据必须放在字典中,通过data参数进行传递
resp = requests.post(url,data=dat)
print(resp.json())  #将服务器返回内容解析成json格式--》字典
复制代码

 

案例1.2:抓取豆瓣电影

这里主要介绍的是使用param封装参数

需求:

  1. 在豆瓣网中,获取到电影的信息
  2. 将数据写入文件夹中

技术:requestsjsonparam封装、数据存储(创建并写入文件)

复制代码
import requests
import json
url = "https://movie.douban.com/j/chart/top_list"
#若将参数直接放在连接后面,则url太长,不美观,这里重新封装参数
param = {
    "type": "24",
    "interval_id": "100:90",
    "action": "",
    "start": 0,  #从电影的第几部开始
    "limit": 20,  #一次取出的个数
}

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'
}

resp = requests.get(url=url,params=param,headers=headers)
list_data = resp.json()
fp = open('./douban.json','w',encoding='utf-8')
json.dump(list_data,fp=fp,ensure_ascii=False)
print('over!!')
复制代码

案例1.3:模拟肯德基餐厅查询

这里用到的是json格式的数据存储

需求:

  1. 模拟肯德基餐厅查询
  2. 将查询到的详细数据存储到文件中

技术:requstsjsondump)、post请求、数据存储

复制代码
# -*-coding:utf-8-*-
import requests
import json

url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword'

header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'
}

keyword = input('input code:')
num = input('input number:')
data = {
    'cname': '',
    'pid': '',
    'keyword': keyword,
    'pageIndex': num,
    'pageSize': 10
}
resp = requests.post(url,data=data,headers=header)

list_data = resp.json()

name = keyword + '.json'
fp = open(name,'w',encoding='utf-8')
json.dump(list_data,fp=fp,ensure_ascii=False) 
#注意要写ensure_ascii=False
print('over!!')
复制代码

第二章:request进阶

1.cookie验证:

爬取某些网页需要携带cookie才可以进行访问

案例2.1:模拟用户登录

要求:

  1. 模拟小说网用户登录
  2. 拿到书架上的数据

技术:requestspost请求、cookie验证、session请求

1)登录——》得到cookie

2)带着cookie去请求到书架url——》数据上的内容

必须把上面的两个操作连起来

我们可以使用session进行请求——session 可以认为是一连串的请求,在这个过程中的cookie不会丢失

复制代码
import requests

"""第一种处理方法"""
session = requests.session()  #会话
data = {
    "loginName": "19183344702",
    "password": "19183344702wsy"
}
#1.登录
url1 = "https://passport.17k.com/ck/user/login"
resp1 = session.post(url1,data=data)
# print(resp.text)
# print(resp.cookies)  #看cookie

#2. 拿书架上的数据

url2 = "https://user.17k.com/ck/user/mine/readList?page=1&appKey=2406394919"

#刚才的那个seeion中是有cookie的
resp2 = session.get(url2)

print(resp2.json())



"""第二种处理方法"""

header = {
    'Cookie': 'GUID=2f4ba5e2-8481-4c06-941f-6a45a4d4d627; BAIDU_SSP_lcr=https://www.baidu.com/link?url=SKgEKu_Pw7txiOqIB2TpmVUc0zTLkPaHZQmET_yn-9a&wd=&eqid=a6806ddb000bb8090000000661d00588&tn=88093251_22_hao_pg; sajssdk_2015_cross_new_user=1; c_channel=0; c_csc=web; Hm_lvt_9793f42b498361373512340937deb2a0=1641022860,1641026842; accessToken=avatarUrl%3Dhttps%253A%252F%252Fcdn.static.17k.com%252Fuser%252Favatar%252F06%252F26%252F19%252F87641926.jpg-88x88%253Fv%253D1641023290000%26id%3D87641926%26nickname%3D%25E5%25AE%259E%25E4%25B9%2583%25E5%259C%25B0%25E5%2591%25BC%25E7%25AD%2589%25E4%25BD%25A0%25E5%2596%259C%25E5%25A5%25BD%26e%3D1656578976%26s%3D91752833797cb8ca; sensorsdata2015jssdkcross=%7B%22distinct_id%22%3A%2287641926%22%2C%22%24device_id%22%3A%2217e1495aa73223-0126cf5a20caf-57b1a33-1327104-17e1495aa746fc%22%2C%22props%22%3A%7B%22%24latest_traffic_source_type%22%3A%22%E8%87%AA%E7%84%B6%E6%90%9C%E7%B4%A2%E6%B5%81%E9%87%8F%22%2C%22%24latest_referrer%22%3A%22https%3A%2F%2Fwww.baidu.com%2Flink%22%2C%22%24latest_referrer_host%22%3A%22www.baidu.com%22%2C%22%24latest_search_keyword%22%3A%22%E6%9C%AA%E5%8F%96%E5%88%B0%E5%80%BC%22%7D%2C%22first_id%22%3A%222f4ba5e2-8481-4c06-941f-6a45a4d4d627%22%7D; Hm_lpvt_9793f42b498361373512340937deb2a0=1641027386'
}
resp = requests.get("https://user.17k.com/ck/user/mine/readList?page=1&appKey=2406394919",headers=header)
print(resp.text)
复制代码

2.防盗链:

案例2.2:抓取梨视频

要求:

  1. 爬取梨视频网的视频数据
  2. 将获取到的数据下载到本地

技术:requestsjsonget请求、replace替换、防盗链

1)拿到contId

2)拿到videoStatus返回的json.——>srcURL

3srcURL里面的内容进行修整

4)下载视频

 

复制代码
import requests

#拿取视频的网址
url = 'https://www.pearvideo.com/video_1748738'
contId = url.split("_")[1]  #通过下划线去切割,取第一个(即_前面为第0个,后面为第1个)

videoStatusUrl = f"https://www.pearvideo.com/videoStatus.jsp?contId={contId}&mrd=0.7361149627995875"
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36',
    #防盗链:溯源,当前本次请求的上一级是谁
    "Referer": "https://www.pearvideo.com/video_1748738"
    #也可以写成 "Referer": "url"
}

resp = requests.get(videoStatusUrl,headers=headers)
# print(resp.text)
dic = resp.json()
srcUrl = dic['videoInfo']['videos']['srcUrl']  #拿到标签videoInfo下的videos下的srcUrl
systemTime = dic['systemTime']

srcUrl = srcUrl.replace(systemTime, f"cont-{contId}") #replace 替换,将逗号前的换成逗号后的
# print(srcUrl)

#下载视频
with open("a.mp4", mode="wb") as f:
    f.write(requests.get(srcUrl).content)
复制代码

3.代理:

原理:通过第三方的一个机器去发送请求

案例2.3:请求百度网站

需求:

  1. 使用代理ip
  2. 对百度发出请求

技术:requestsget请求、代理ip

 

复制代码
#原理:通过第三方的一个机器去发送请求
import requests

#可取网站上找免费代理IP,推荐适应透明类型

#120.220.220.95:8085
proxies = {
    "http": "https://120.220.220.95:8085"
}

resp = requests.get("https://www.baidu.com", proxies=proxies)  #加上代理
resp.encoding = "utf-8"
print(resp.text)
复制代码

 

第三章:数据解析

我们从网页中爬取到的内容是html代码,而我们正真想要的是夹在html里面的内容,这就涉及到数据提取的问题

三种解析方式:

1. re解析——正则表达式

案例3.1.1:爬取豆瓣top250电影排行

需求:

  1. 爬取豆瓣top250电影排行
  2. 将爬取的数据以csv形式进行存储

技术:requestsget请求、re数据解析、csv存储

通过分析发现,我们想要的内容(名字,年份,评分,评分人数)直接在页面源代码中就能找到

 

复制代码
import requests  #拿到页面源代码
import re  #通过re来提取
import csv

url = "https://movie.douban.com/top250"

header = {
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36"
}

resp = requests.get(url, headers=header)

page_content = resp.text

# 解析数据
obj = re.compile(r'<li>.*?<div class="item">.*?<span class="title">(?P<name>.*?)'
                 r'</span>.*?<br>(?P<year>.*?)&nbsp.*?<span class="rating_num" property="v:average">'
                 r'(?P<score>.*?)</span>.*?<span>(?P<num>.*?)人评价</span>', re.S) #将需要的数据用括号括起来

#开始匹配
result = obj.finditer(page_content)  #匹配内容,得到的是迭代器
f = open("data.csv", mode="w", encoding="utf-8") #将结果存为csv文件
csvwriter = csv.writer(f)  #csv文件,一般以逗号分隔
for it in result:
    # print(it.group("name"))
    # print(it.group("year").strip())  #去掉年份前面的空格
    # print(it.group("score"))
    # print(it.group("num"))
    dic = it.groupdict()  #将内容整理成字典的格式
    dic['year'] = dic['year'].strip()
    csvwriter.writerow(dic.values())

f.close()
print("over!")
复制代码

 

案例3.1.2

需求:爬取电影天堂信息

  1. 爬取电影天堂2020必看片的下载地址

技术:requestsget请求、re解析、预加载

1)定位到2020必看片

2)从2020必看片中提取到子页面的链接地址

3)请求子页面的链接地址,拿到我们想要的下载地址..

先拿到所有的<li>中的内容,再拿到所有的子页面连接

 

 

                         图1

复制代码
import requests
import re

domain = "https://www.dytt89.com/"
resp = requests.get(domain, verify=False)  #verify=False去掉安全验证
#默认为utf-8,但这里的charset=gb2312
resp.encoding = 'gb2312' #指定字符集

# print(resp.text)

#拿到ul里面的li
#预加载
obj1 = re.compile(r'2021必看热片.*?<ul>(?P<ul>.*?)</ul>',re.S)
obj2 = re.compile(r"<a href='(?P<href>.*?)'",re.S)
obj3 = re.compile(r'◎片  名(?P<movie>.*?)<br />.*?'
                  r'<td style="WORD-WRAP: break-word" bgcolor="#fdfddf"><a href="'
                  r'(?P<download>.*?)">',re.S)

result1 = obj1.finditer(resp.text)
child_href_list = []
for it in result1:
    ul = it.group('ul')
    # print(ul)

    #提取子页面连接
    result2 = obj2.finditer(ul)
    for itt in result2:
        #拼接子页面的url地址:域名 + 子页面地址
        child_href = domain + itt.group('href').strip("/")  #strip("/")去掉前面的斜杠
        child_href_list.append(child_href)  #把子页面连接添加到列表中
        # print(itt.group('href'))

# #提取子页面内容
for href in child_href_list:
    child_resp = requests.get(href,verify=False)
    child_resp.encoding = 'gb2312'
    # print(child_resp.text)
    result3 = obj3.search(child_resp.text)  #search:拿到第一次匹配的内容
    print(result3.group('movie'))
    print(result3.group('download'))
    # break  #测试用
复制代码

2. bs4解析

安装:

 pip install bs4

案例3.2:下载图片

需求:

  1. 爬取优美图库的图片
  2. 将图片下载下来

技术:requestsget请求、bs4解析、time、数据存储

1)拿到主页面的页面源代码,然后提取到子页面的链接地址,href

2)通过href拿到子页面的的内容,从子页面中找到图片的下载地址 img--src

3)下载图片

 

复制代码
import requests
from bs4 import BeautifulSoup
import time
url = "https://www.umei.cc/bizhitupian/weimeibizhi/"

resp = requests.get(url)
resp.encoding = 'utf-8'  #处理乱码

# print(resp.text)
#把源代码交给bs
main_page = BeautifulSoup(resp.text, "html.parser")
alist = main_page.find("div", class_="TypeList").find_all("a")  #把范围缩小,这里的class是标识符,为了区别,所以要在后面加上下划线
# print(alist)
url1 = "https://www.umei.cc/"
for a in alist:
    href = url1 + a.get('href')  #直接通过get就可以拿到属性的值,并拼接,形成子页面连接
    # print(href)
    #拿到子页面的源代码
    child_resp = requests.get(href)
    child_resp.encoding = 'utf-8'
    child_resp_text = child_resp.text

    #从子页面中拿到图片的下载路径
    child_page = BeautifulSoup(child_resp_text,"html.parser")
    p = child_page.find("p",  align="center")
    # print(p)
    img = p.find('img')
    src = img.get('src')
    # print(src)  #拿到img下的src属性

    # break  #测试

    #下载图片
    img_resp = requests.get(src)
    # img_resp.content  #这里拿到的是字节
    img_name = src.split('/')[-1]  #拿到url中的最后/以后的内容
    with open("img/"+img_name,mode='wb') as f:
        f.write(img_resp.content)  #将图片的内容写入文件

    print("over!",img_name)
    time.sleep(1)  #由于不停地访问子页面,为了防止服务器不把你干掉,在这里让他休息1秒钟左右

print("all_over!")
复制代码

下图是下载好的图片:

 

 

 

2

 

3.xpath解析

 

安装lxml模块:

pip install xpath

案例3.3:抓取猪八戒网信息

需求:

  1. 爬取猪八戒招聘网的招聘信息

技术:requestslxmlxpath解析、join

1)拿页面源代码

2)数据解析

 

复制代码
import requests
from lxml import etree
#etree可以直接加载html文件

url = "https://guangyuan.zbj.com/search/f/?kw=saas"

resp = requests.get(url)

# print(resp.text)

html = etree.HTML(resp.text)  #etree.HTML:将html源码加载出来
#注意:HTML与XML不能混用

#拿到每一个服务商的div
divs = html.xpath("/html/body/div[6]/div/div/div[2]/div[5]/div[1]/div")
for div in divs: #每一个服务商信息
    price = div.xpath("./div/div/a[2]/div[2]/div[1]/span[1]/text()")[0].strip("¥")  #去掉¥符号
    title = "saas".join(div.xpath("./div/div/a[2]/div[2]/div[2]/p/text()"))  #将内容用saas拼接
    com_name = div.xpath("./div/div/a[1]/div[1]/p/text()")
    location = div.xpath("./div/div/a[1]/div[1]/div/span/text()")[0]  #把内容从列表中拿出
    print(price)
复制代码

 

第四章:selenium基础

selenium:自动化测试工具

可以:打开浏览器,然后像人一样去操作浏览器

程序员可以从selenium中直接提取网页上的各种信息

环境搭建:

pip install selenium -i 清华源

下载浏览器驱动:

http://chromedriver.storage.googleapis.com/index.html

下载与chrome浏览器相匹配的chromedriver版本,解压后,将chromedriver.exe放置到python的目录下;然后把驱动的路径添加到环境变量path

可根据图3,图4,图5进行浏览器驱动的下载和配置:

3

4

 

 

5

 

5

 

创建创建第一个浏览器对象,并打开网址

 

from  selenium.webdriver import Chrome
#1.创建浏览器对象
web = Chrome()
#2.打开一个网址
web.get("http://www.baidu.com")

 

运行程序,弹出网页:

 

 

 

 

 

6

 

案例4.1:抓取拉钩信息

 

需求:

 

  1. 爬取拉钩网的招聘信息

 

技术:seleniumkeytimexpath解析

 

复制代码
from selenium.webdriver import Chrome
#想要输入一个键盘上的按钮
from selenium.webdriver.common.keys import Keys
import time


#1.创建浏览器对象
web = Chrome()

web.get("http://lagou.com")

#找到某个元素,点击它
el = web.find_element_by_xpath('//*[@id="changeCityBox"]/p[1]/a')
time.sleep(1)
el.click() #点击事件

time.sleep(1)  #让浏览器缓一会

#找到输入框,输入Python =》 输入回车/点击搜索按钮
web.find_element_by_xpath('//*[@id="search_input"]').send_keys("python", Keys.ENTER)
time.sleep(1)

#查找存放数据的位置,进行数据提取
#找到页面存放数据的所有div
div_list = web.find_elements_by_xpath('//*[@id="jobList"]/div[1]/div')
print(div_list)

time.sleep(1)
for div in div_list:
    job_name = div.find_element_by_xpath('./div[1]/div[1]/div[1]/a').text
    job_price = div.find_element_by_xpath('./div[1]/div[1]/div[2]/span').text
    job_company = div.find_element_by_xpath('./div[1]/div[2]/div[1]/a').text

    print(job_company,job_name,job_price)
复制代码

 

 

 

 

这里注意find_element_by_find_elements_by_的区别:

 

 

7

 

案例4.2:实现窗口的切换

 

需求:

 

  1. 爬取拉钩网信息,以及子页面的详情信息

 

技术:seleniumtimexpath解析、窗口切换

 

复制代码
import time
from selenium.webdriver.common.keys import Keys

from selenium.webdriver import Chrome

web = Chrome()

web.get("http://lagou.com")

web.find_element_by_xpath('//*[@id="changeCityBox"]/p[1]/a').click()

time.sleep(1)

web.find_element_by_xpath('//*[@id="search_input"]').send_keys('python',Keys.ENTER)

time.sleep(1)

web.find_element_by_xpath('//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a').click()

#如何进入到新窗口中进行提取
#注意,在selenium的眼中,新窗口默认是不切换过来的
web.switch_to.window(web.window_handles[-1])  #表示将selenium调整到最后一个窗口(即新打开的窗口)

#在新窗口中提取内容
job_detail = web.find_element_by_xpath('//*[@id="job_detail"]/dd[2]/div/p').text
print(job_detail)
time.sleep(2)

#关掉子窗口
web.close()
#变更selenium的窗口视角,回到原来的窗口
web.switch_to.window(web.window_handles[0])
print(web.find_element_by_xpath('//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a').text)

# #如果页面遇到iframe如何处理
# web.get("https://www.91kanju.com/vod-play/541-2-1.html")
#
# #处理iframe的话,必须先拿到iframe,然后切换视角到iframe,再然后才可以拿到数据
# iframe = web.find_element_by_xpath('//*[@id="player_iframe"]')
# web.switch_to.frame(iframe)  #切换到iframe
# # web.switch_to.default_content()  #切换回原页面
# tx = web.find_element_by_xpath('/html/head/title').text
# print(tx)
复制代码

 

 

selenium的眼中,新窗口默认是不切换过来的,我们想要用.switch_to.window手动切换他,调整到最后一个窗口(即新打开的窗口)

 

案例4.3:抓取艺恩电影信息

 

这里主要介绍的是:无头浏览器

 

需求:

 

  1. 抓取艺恩网的电影信息
  2. 拿到电影排行榜的电影名称、上映时间、等详情信息

 

技术:seleniumchrome,Options)、timexpath解析

 

 

复制代码
from selenium.webdriver import Chrome
import time
from selenium.webdriver.chrome.options import Options

#准备好参数配置
opt = Options()
opt.add_argument("--headless")
opt.add_argument("--disbale-gpu")


web = Chrome(chrome_options=opt)  #把参数配置设置到浏览器中

web.get("https://ys.endata.cn/BoxOffice/Ranking")
time.sleep(1)

trs = web.find_elements_by_xpath('//*[@id="app"]/section/main/div/div[1]/div/section/section/section/section/div/div[3]/table/tbody/tr')
for tr in trs:
    movie_name = tr.find_element_by_xpath('./td[2]/div/label/data').text
    release_time = tr.find_element_by_xpath('./td[3]/div/label').text
    Cumulative_box_office = tr.find_element_by_xpath('./td[4]/div/label').text

    print(movie_name,release_time,Cumulative_box_office)


print("运行完毕")
web.close()

# #如何拿到页面源代码Elements(经过数据加载以及js执行之后的结果的html内容)
# print(web.page_source)
复制代码

 

案例4.4:登录验证码的处理

这里超级鹰举例

需求:

  1. 使用selenium获取网站数据时,会遇到一些网站需要登录以及验证
  2. 本案例将介绍如何填入用户名,密码,处理验证码

技术:seleniumtimexapthkey

 

复制代码
from selenium.webdriver import Chrome
from chaojiying import Chaojiying_Client
import time

web = Chrome()

web.get("http://www.chaojiying.com/user/login/")

#处理验证码
img = web.find_element_by_xpath('/html/body/div[3]/div/div[3]/div[1]/form/div/img').screenshot_as_png  #截取屏幕存为png格式
chaojiying = Chaojiying_Client('19183344702', '19183344702wsy', '927505')
dic = chaojiying.PostPic(img,1902)
verify_code = dic['pic_str']

#向页面填入用户名,密码,验证码
web.find_element_by_xpath('/html/body/div[3]/div/div[3]/div[1]/form/p[1]/input').send_keys("19183344702")
web.find_element_by_xpath('/html/body/div[3]/div/div[3]/div[1]/form/p[2]/input').send_keys("19183344702wsy")
web.find_element_by_xpath('/html/body/div[3]/div/div[3]/div[1]/form/p[3]/input').send_keys(verify_code)
time.sleep(4)

#点击登录
web.find_element_by_xpath('/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input').click()
复制代码

 

第五章:知识点补充

1. pymysql.Connect()参数说明

host(str):      MySQL服务器地址

port(int):      MySQL服务器端口号

user(str):      用户名

passwd(str):    密码

db(str):        数据库名称

charset(str):   连接编码

2. connection对象支持的方法

cursor()        使用该连接创建并返回游标

commit()        提交当前事务

rollback()      回滚当前事务

close()         关闭连接

3. cursor对象支持的方法

execute(op)     执行一个数据库的查询命令

fetchone()      取得结果集的下一行

fetchmany(size) 获取结果集的下几行

fetchall()      获取结果集中的所有行

rowcount()      返回数据条数或影响行数

close()         关闭游标对象

4.selenium常用方法整理

1常用库导入

1.from selenium import webdriver 导入webdriver模块

2.from selenium.webdriver import ActionChains 导入动作链类,动作链可以储存鼠标的动作,并一起执行

3.from selenium.webdriver.common.key import Key 键盘操作使用的是Keys,一般配合send_keys使用

4.from selenium.webdriver.support.select import Select 下拉框的操作都交由Select类进行处理

5.from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC 显示等待使用的类

2基本操作

2.1.浏览器相关操作

创建浏览器对象    driver = webdriver.xxx()

窗口最大化    maximize_window()

获取浏览器尺寸    get_window_size()

设置浏览器尺寸    set_window_size()

获取浏览器位置    get_window_position()

设置浏览器位置    set_window_position(x,y)

关闭当前标签/窗口    close()

关闭所有标签/窗口     quit()

2.2.页面相关操作

请求某个url     driver.get(url)

刷新页面操作     refresh()

回退到之前的页面     back()

前进到之后的页面     forward()

获取当前访问页面     urlcurrent_url

获取当前浏览器标题     title

保存图片     get_screenshot_as_png()/get_screenshot_as_file(file)

网页源码     page_source

2.3.页面元素的定位(八种定位方式)

id定位     driver.find_element_by_id(value)

name属性值定位     driver.find_element_by_name(value)

类名定位     driver.find_element_by_class_name(value)

标签名定位     driver.find_element_by_tag_name(value)

链接文本定位     driver.find_element_by_link_text(value)

部分链接文本     driver.find_element_by_partial_link_text(value)

xpath路径表达式     driver.find_element_by_xpath(value)

css选择器     driver.find_element_by_css_selector(value)

2.4.元素的操作

对元素的相关操作,一般要先获取到元素,再调用相关方法 element = driver.find_element_by_xxx(value)

点击操作     element.click()

清空输入框     element.clear()

输入框输入数据     element.send_keys(data)

获取文本内容(既开闭标签之间的内容)     element.text

获取属性值(获取element元素的value属性的值)     element.get_attribute(value)

2.5.鼠标和键盘操作

鼠标操作需要导入类,见第一部分,然后创建对象ActionChains(driver),键盘操作导入类见第一部分

2.5.1.常用鼠标操作:

鼠标右击:

el = driver.find_element_by_xxx(value)

context_click(el)

 

鼠标双击:

el = driver.find_element_by_xxx(value)

ActionChains(driver).double_click(el).perform()

 

鼠标悬停:

el = driver.find_element_by_xxx(value)

ActionChains(driver).move_to_element(el).perform()

 

2.5.2.常用键盘操作:

send_keys(Keys.BACK_SPACE) 删除键(BackSpace

send_keys(Keys.SPACE) 空格键(Space)

send_keys(Keys.TAB) 制表键(Tab)

send_keys(Keys.ESCAPE) 回退键(Esc

send_keys(Keys.ENTER) 回车键(Enter

send_keys(Keys.CONTROL,‘a’) 全选(Ctrl+A

send_keys(Keys.CONTROL,‘c’) 复制(Ctrl+C

send_keys(Keys.CONTROL,‘x’) 剪切(Ctrl+X

send_keys(Keys.CONTROL,‘v’) 粘贴(Ctrl+V

2.6.弹出框操作

进入到弹出框中:     driver.switch_to.alert()

接收警告:     accept()

关闭警告:     dismiss()

发送文本到警告框:     send_keys(data)

2.7.下拉框操作

将定位到的下拉框元素传入Select类中:     selobj = Select(element)

通过索引选择,index 索引从 0 开始:     select_by_index()

通过值选择(option标签的一个属性值)    select_by_value()

通过文本选择(下拉框的值)    select_by_visible_text()

查看所有已选:     all_selected_options

查看第一个已选:     first_selected_option

查看是否是多选:     is_multiple

查看选项元素列表:     options

取消选择:      deselect_by_index() /deselect_by_value()/ deselect_by_visible_text()

2.8.滚动条操作

js = "window.scrollTo(x,y) " x为水平拖动距离,y为垂直拖动举例

driver.execute_script(js)

js= "var q=document.documentElement.scrollTop=n" n为从顶部往下移动滚动举例

driver.execute_script(js)

2.9.cookies操作

获取所有cookies:      get_cookies()

获取key对应的值:      get_cookie(key)

设置cookies:      add_cookie(cookie_dict)

删除指定名称的cookie:      delete_cookie(name)

删除所有cookie:      delete_all_cookies()

2.10.多标签/多窗口、多表单/多框架切换

多表单/多框架切换:

直接使用id值切换进表单:       driver.switch_to.frame(value)

定位到表单元素,再切换进入:

el = driver.find_element_by_xxx(value)

driver.switch_to.frame(el)

跳回最外层的页面:      driver.switch_to.default_content()

跳回上层的页面:      driver.switch_to.parent_frame()

 

多标签/多窗口之间的切换:

获取所有窗口的句柄:       handles = driver.window_handlers

通过窗口的句柄进入的窗口:      driver.switch_to.window(handles[n])

补充:

修改selenium的版本:

pip install --upgrade selenium==3.3.1

第六章:综合案例

案例6.1:爬取网易云音乐评论信息

需求:

  1. 这里想要拿到网易云音乐评论的信息

技术:AESbase64requestsjson、处理加密过程

通过分析,我发现在页面源代码中拿不到我想要的评论信息

思路:

1)找到未加密的参数   #window.asrsea(参数,xxx,xxx,xxx

2)想办法把参数进行加密(必须参考网易的逻辑),params==encText,encSecKey==encSecKey

3)请求到网易,拿到评论信息

下面是一些找数据的过程:
首先找到我们需要的数据所对应的位置:

                    

8

 

这是加密后的结果

                    

9

initiator中可以看到,发送请求后,js脚本执行的过程:

                    

10

这里有个小技巧,可以使大片的代码变得更好看:

                    

11

 

 

                    

12

找到一个断点,刷新:

                    

13

一直执行,找到想要的url

                    

14 

这里观察发现,此处的数据已经被加密了,所以要往回找:

                    

 

                                            图15

  

16

                    

17

找到参数被进行加密操作的哪一步:  

                    

18

                    

19

重新选择断点,去掉之前的断点:

                    

20

分析处理加密的过程

复制代码
"""
整个的加密过程,入口是d
   function a(a = 16) {  #产生的是随机的16位字符串
        var d, e, b = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", c = "";
        for (d = 0; a > d; d += 1)  #循环16次
            e = Math.random() * b.length,   #随机数   1.2345
            e = Math.floor(e),    #取整  1
            c += b.charAt(e);  #取字符串中的xxx位置  b[1]=b
        return c
    }
    function b(a, b) {  #a是要加密的内容
        var c = CryptoJS.enc.Utf8.parse(b)  #?b 就是秘钥
          , d = CryptoJS.enc.Utf8.parse("0102030405060708")
          , e = CryptoJS.enc.Utf8.parse(a)  #e是数据
          , f = CryptoJS.AES.encrypt(e, c, {  #AES加密  c:加密的秘钥
            iv: d,  #iv:偏移量
            mode: CryptoJS.mode.CBC  #模式:CBC
        });
        return f.toString()
    }
    function c(a, b, c) {   #c里面不产生随机数
        var d, e;
        return setMaxDigits(131),
        d = new RSAKeyPair(b,"",c),  #RSA加密
        e = encryptedString(d, a)
    }
    function d(d, e, f, g) {  #d:数据 ,e:010001,f:很长的定值,g:0CoJUm6Qyw8W8jud
        var h = {}  #空对象
          , i = a(16);  #i就是一个16位的随机值,把i设置成定值
        h.encText = b(d, g),   #g是秘钥
        h.encText = b(h.encText, i),  #返回的就是params  i也是秘钥
        h.encSecKey = c(i, e, f),   #得到的就是encSecKey,e和f是定死的,如果此时i固定,则得到key一定是固定的值
        return h
    }
"""
# 两次加密:
# 数据+g =》b =>第一次加密+i =>b = params
复制代码

需要安装pycrypto:

 pip install pycrypto

代码:

复制代码
# -*-coding:utf-8-*-

# 需要安装pycrypto:  pip install pycrypto

from Crypto.Cipher import AES
from base64 import b64encode
import requests
import json

url = "https://music.163.com/weapi/comment/resource/comments/get?csrf_token="

#请求方式post
#找到的真实参数
data = {
    'csrf_token': "",
    'cursor': "-1",
    'offset': "0",
    'orderType': "1",
    'pageNo': "1",
    'pageSize': "20",
    'rid': "R_SO_4_1907820140",
    'threadId': "R_SO_4_1907820140"
}
#服务与d的
f = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7'
g = '0CoJUm6Qyw8W8jud'
e = '010001'
i = "Rx02e4MnWHw8B5Cg"  #手动固定的 ——》人家函数中是随机的

def get_encSecKey():  #由于i是固定的,那么encSecText就是固定的  c()函数的结果就是固定的
    return "9645167e8cb73010bdf53ed7d79242c3933c71372bc1612a427df9d2b31b4b85c32a055f5053eff5feef1a934869265610117bf3ed7fba347adf3222c12f37d47cce61c32839faf624668a09fd15c25b974d4762b365ca4701eb0f2bf14edd70753de0158653d0450ac8279a7d94db0d3002695d131d3c2984b68ad9993e09b4"

#把参数进行两次加密
def get_params(data):  #默认这里接受到的是字符串
    first = enc_params(data, g)
    second = enc_params(first, i)
    return second  #返回的是params

#准话16的倍数,为下方的加密算法服务
def to_16(data):
    pad = 16 - len(data) % 16
    data += chr(pad) * pad
    return data

 #加密过程
def enc_params(data,key):
    iv = "0102030405060708"
    data = to_16(data)
    aes = AES.new(key=key.encode("utf-8"), IV=iv.encode('utf-8'), mode=AES.MODE_CBC) #创建加密器
    bs = aes.encrypt(data.encode("utf-8"))   #加密  ,加密的内容的长度必须是16的倍数,它有自己规定的逻辑来补齐
    return str(b64encode(bs), "utf-8")  #转化成字符串返回

#发送请求,得到评论结果
resp = requests.post(url, data={
    "params": get_params(json.dumps(data)),  #json.dumps()将字典变为字符串
    "encSecKey": get_encSecKey()
})
print(resp.text)
复制代码

第七章:scrapy学习

7.1.Scrapy架构图

 

 

 

调度器(Scheduler):
调度器,说白了把它假设成为一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是 什么,同时去除重复的网址(不做无用功)。用户可以自己的需求定制调度器。

 

下载器(Downloader):

下载器,是所有组件中负担最大的,它用于高速地下载网络上的资源。Scrapy的下载器代码不会太复杂,但效率高,主要的原因是Scrapy下载器是建立在twisted这个高效的异步模型上的(其实整个框架都在建立在这个模型上的)

 

爬虫(Spider:

爬虫,是用户最关心的部份。用户定制自己的爬虫(通过定制正则表达式等语法),用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。 用户也可以从中提取出链接,Scrapy继续抓取下一个页面。

 

管道文件(Item Pipeline):

实体管道,用于处理爬虫(spider)提取的实体。主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。

 

Scrapy引擎(Scrapy Engine):

Scrapy引擎是整个框架的核心.它用来控制调试器、下载器、爬虫。实际上,引擎相当于计算机的CPU,它控制着整个流程。

 

功能:高性能的持久化存储,异步的数据下载,高性能的数据解析,分布式等

 

环境安装:

mac or linux

- pip install csrapy

windows

- pip install wheel

 

- 下载twisted,下载地址:https://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted

- 安装twisted

pip install Twisted-17.1.0-cp36m-win_amd64.whl
- pip install pywin32
- pip install scrapy

- 测试:在终端里录入scrapy指令,没有报错即表示安装成功

这里需要注意:需要先进入下载twisted的目录下再执行安装命令

最后安装成功后可以看到:

                    

22

7.2.Scrapy基本使用

创建一个工程: pycharm终端中,进入创建的工程目录想要放置的文件夹下,输入以下代码

#scrapy startproject xxxpro

scrapy startproject firstBlood

创建成功后可以看到这些东西:

                          

23

spiders:爬虫文件夹(爬虫目录),此文件夹中必须要存放一个原文件|
settings:配置文件,里面放置一些我们相关的配置
items:封装了一个item类型的类
pipelines:专门用来处理item类型对象,该方法可以接收爬虫文件提交过来的item对象,将数据持久化存储到本地或数据库

 

spiders子目录中创建一个爬虫文件:

首先要进入到工程目录当中:cd  xxxpro

>>> cd firstBlood/
#scrapy genspider spiderName www.xxx.com
>>> scrapy genspider first www.xxx.com

创建成功后出现:

                      

24

                              

25

first.py文件文件下的操作:

复制代码
import scrapy

class FirstSpider(scrapy.Spider):  #Spider父类
    name = 'first'      #爬虫文件的名称:就是爬虫源文件的唯一标识,不能重复
    #allowed_domains = ['www.baidu.com']       #允许的域名:用来限定start_urls列表中那些url可以进行请求发送, 通常情况下不用
    start_urls = ['https://www.baidu.com/', 'https://www.baidu.com']    #起始的url列表:该列表中存放的url会被scrapy自动进行请求的发送,可以有多个

    #用作于数据解析:response参数表示的就是请求成功后对应的响应对象
    def parse(self, response):  #被调用的次数有start_urls中的url个数决定
        print(response)
复制代码

执行工程:

#scrapy crawl spiderName
>>> scrapy crawl first
>>> scrapy crawl first --nolog  #--nolog表示只打印print输出的内容。需要注意的是:如果程序报错,它输出为空,没有提示信息

一般不建议使用 --nolog,而是去settings.py文件中添加这行代码:

#输出显示指定类型的日志信息
LOG_LEVEL = 'ERROR'

除此之外,setting.py文件中还有一处需要修改的地方:

ROBOTSTXT_OBEY = False  #表示是否遵从robots协议,为Ture的话,很多网站都不能请求到,所以要改成False

7.3.Scrapy数据解析

案例7.3.1:爬取糗事百科数据

需求:

  1. 这里想要爬取到糗事百科的用户评论信息

技术:scrapyxpath

我想要拿到的是作者的名称和他所发布的段子的内容
#首先创建一个scrapy工程
>>> scrapy startproject qiubaiPro
#cd qiubaiPro文件夹下,新建一个爬虫文件
>>> scrapy genspider qiubai www.xxx.com

分析网页源代码,找到想要拿到的信息:

                    

26

qiubai.py文件下的操作:

复制代码
import scrapy

class QiubaiSpider(scrapy.Spider):
    name = 'qiubai'
# allowed_domains = ['www.xxx.com']  将这行注释掉

    # 修改起始url
    # start_urls = ['http://www.xxx.com/']
start_urls = ['https://www.qiushibaike.com/text/']

    def parse(self, response):
        #解析:作者的名称+段子的内容
        #在scapy中我们不需要提前获取响应数据,可以直接调用xpath()方法,基于xpath模式实现数据解析
        div_list = response.xpath('//div[@class="col1 old-style-col1"]/div')  #这里通过class="col1 old-style-col1"进行定位
        for div in div_list:

            # 所有xpath返回的类型为字符串,若需要返回列表,则在后面加上[0],但若是//text()后面就不能加[0]
            # #xpath返回的是列表,但是列表元素一定是Selector类型的对象
            #extract可以将Selector对象中国的data参数存储的字符串提取出来
            """author = div.xpath('./div[1]/a[2]/h2/text()')[0].extract()"""

            #extract_first()使用条件:保证xapth返回的列表中只有一个列表元素
            author = div.xpath('./div[1]/a[2]/h2/text()').extract_first()  #将列表中的第0个元素所对应的Selector进行extract操作,此时返回的就是字符串,而不是列表

            # 这里需要注意,若文本中还要字标签,需要使用//text()
            #列表调用了extract之后,则表示列表中每一个Selector对象中的data对应的字符串提取了出来
            content = div.xpath('./a/div/span//text()').extract()

            #将列表转为字符串,并以规定的形式连接
            content = ''.join(content)

            print(author,content)
            break #测试


在settings.py下的操作:
#需要将我们所需要爬取的网站的user_agent放到这里
#USER_AGENT = 'qiubaiPro (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'

# Obey robots.txt rules
#True改为False
ROBOTSTXT_OBEY = False

#添加
LOG_LEVEL = 'ERROR'

复制代码

7.4.Scrapy持久化存储

7.4.1.基于管道(常用)

编码流程:

将数据解析

item类中定义相关的属性

将解析的数据封装存储到item类型的对象中

item类型的对象提交给管道进行持久化存储的操作

在管道的proxess_item重要将其接受到的item对象存储的数据进行持久化存储操作

在配置文件中开启管道

#定义author 和 content 这两个属性
author = scrapy.Field()
content = scrapy.Field()
#pass

qiubai.py文件中:

复制代码
#将items.py中的类导入
from qiubaiPro.items import QiubaiproItem
#1. 数据解析

#2.实例化一个item类型的对象
item = QiubaiproItem()
#将author,content封装到item类型对象中
item['author'] = author
item['content'] = content

#3.将item提交到管道
yield item
复制代码

pipelines.py文件中:

复制代码
class QiubaiproPipeline:
    fp = None
    #重写父类方法:该方法只会在开始爬虫的时候被调用一次
    def open_spider(self,spider):
        print("开始爬虫....")
        #打开文件
        self.fp = open('./qiubai.txt','w',encoding='utf-8')

    #专门用来处理item类型对象
    #该方法可以接收爬虫文件提交过来的item对象
    #该方法每接收到一个item就会被调用一次
    def process_item(self, item, spider):
        author = item['author']
        content = item['content']

        #将数据写入文件
        self.fp.write(author+':'+content+'\n')
        return item

    def close_spider(self,spider):
        print("结束爬虫!")
        #关闭文件
        self.fp.close()
复制代码

settings.py文件中将以下代码的注释去掉:

ITEM_PIPELINES = {
   'qiubaiPro.pipelines.QiubaiproPipeline': 300,  #300表示的是优先级,数值越小优先级越高
}

最后在终端执行:

scrapy crawl qiubai

生成一个文件:

                            

                                          27 

注意:匿名用户解析的路径和普通用户不同,我们需要再并列一个路径,即

author = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span/h2/text()').extract_first()

优缺点:通用性强,但编码时比较繁琐

将爬取到的数据一份存储到本地一份存储到数据库 基于上面案例,需要在pipelines.py文件中再增加一个类:

复制代码
#将数据存储到mysql中
class mysqlPileLine():
    conn = None
    cursor = None
    def open_spider(self,spider):
        # 创建连接对象
        self.conn = pymysql.connect(host='127.0.0.1',port=3306,user='root',password='19183344702',db='Python')
    def process_item(self,item,spider):
        # 创建游标对象
        self.cursor = self.conn.cursor()
        try:
            #执行sql语句
            self.cursor.execute('insert into qiubai valuse("%s","%s")'%(item["author"],item["content"]))
            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()
复制代码

seetings.py文件下:

ITEM_PIPELINES = {
   'qiubaiPro.pipelines.QiubaiproPipeline': 300,
   'qiubaiPro.pipelines.mysqlPileLine': 301,
}

最后运行执行工程,数据就被保存到mysql中了。

注意: 管道文件中一个管道类对应的是将数据存储到一种平台 爬虫文件提交的item只会给管道文件的第一个被执行的管道类接受 process_item中的return item表示将item传递给洗衣歌即将被执行的管道类最后运行执行工程,数据就被保存到mysql中了。

注意: 管道文件中一个管道类对应的是将数据存储到一种平台 爬虫文件提交的item只会给管道文件的第一个被执行的管道类接受 process_item中的return item表示将item传递给洗衣歌即将被执行的管道类

7.4.2.基于终端指令

指令:scrapy crawl xxx -o filePath

要求:只可以将parse方法的返回值存储到本地的文本文件中

在上面的基础上,将数据持久化到文件:

复制代码
#1.首先定义一个列表,用于存储所有解析到的数据
all_data = []

#2.在循环结构中添加以下代码
 #先将返回的值封装成字典
            dic = {
                'author':author,
                'content':content
            }
            all_data.append(dic)  #将所有拿到的数据添加到all_data中

#3.最后返回列表
return all_data

#4.在终端执行程序
>>> scrapy crawl qiubai -o ./qiubai.csv  #-o 文件名:表示将数据存储到这个文件中
复制代码

命令执行后,就可以看到生成的文件:

                            

                                           28 

注意:基于终端指令存储数据只能存为这几种文件格式:

'json','jsonlines''jl''csv''xml''marshal''pickle'

优缺点:简介高效便捷,但局限性比较强(数据只可以存储到指定后缀的文本文件中)

7.5.基于Spider的全栈数据爬取

就是将网站中板块下的全部页码对应的页面数据进行爬取

案例7.5.1:爬取唯美壁纸的图片的名称

需求:

1. 爬取唯美壁纸的图片名称

技术:scrapyxpathresponseyield

实现思路:所有页面的url添加到start_urls列表(不推荐) 自行手动进行请求发送(推荐) 手动请求发送:

yield scrapy.Request(url=,callback)  #callback专门用于数据解析
复制代码
class BizhiSpider(scrapy.Spider):
    name = 'bizhi'
    # allowed_domains = ['www.xxx.com']

    start_urls = ['http://www.netbian.com/weimei/index.htm']

    #生成一个通用的url模板(不可变)
    url = 'http://www.netbian.com/weimei/index_%d.htm'
    page_num = 2

    def parse(self, response):
        li_list = response.xpath('//*[@id="main"]/div[3]/ul/li')
        for li in li_list:
            img_name = li.xpath('./a/b/text()').extract_first()
            print(img_name)


        if self.page_num <= 3:
            new_url = format(self.url%self.page_num)  #format 格式化输出
            self.page_num += 1
            #手动请求发送,callback回调函数是专门用作于数据解析
            yield scrapy.Request(url=new_url,callback=self.parse)
复制代码

7.6.请求传参

使用场景:如果爬取解析额数据不在同一张页面中。(深度爬取)

案例7.6.1:爬取boss的岗位名称,岗位描述

需求:

1. 爬取boss的岗位名称和岗位描述

技术:scrapyresponsexpathyieldrequestjoin

boss.py文件:

复制代码
import scrapy
from bossPro.items import BossproItem

class BossSpider(scrapy.Spider):
    name = 'boss'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://www.zhipin.com/c100010000-p100109/?ka=search_100109']
    url = 'https://www.zhipin.com/c100010000-p100109/?page=%d'
page_num = 2

    #回调函数接收item
    #用于解析详情页的岗位信息
    def parse_detail(self,response):
        #请求传参
        item = response.meta['item']
        job_desc = response.xpath('//*[@id="main"]/div[3]/div/div[2]/div[2]/div[1]/div//text()').extract()
        job_desc = ''.join(job_desc)
        # print(job_desc)
        item['job_desc'] = job_desc
        yield item

    #用于解析首页的岗位名称
    def parse(self, response):
        li_list = response.xpath('//*[@id="main"]/div/div[3]/ul/li')
        for li in li_list:
            item = BossproItem()
            job_name = li.xpath('.//div[@class="job-title"]/span[1]/a/text()').extract_first()
            item['job_name'] = job_name
            # print(job_name)
            detail_url = 'https://www.zhipin.com/'+li.xpath('..//div[@class="job-title"]/span[1]/a/@href').extract_first()
            #对详情页发请求获取详情页的页面源码数据
            #手动请求的发送
            #请求传参:mate={},meta可以将它所对应的字典传递给request的回调函数,即parse_detail
            yield scrapy.Request(detail_url,callback=self.parse_tetail,meta={'item':item})
        #进行分页操作
        if self.page_num <= 3:
            new_url = format(self.url%self.page_num)
            self.page_num += 1

            yield scrapy.Request(new_url,callback=self.parse)
复制代码

7.7.图片数据爬取之imagespipeline

基于scrapy爬取字符串类型的数据和爬取图片类型的数据区别:

字符串:只需要基于xpath进行解析且提交管道进行持久化存储

图片:xpath解析出图片src的属性值,单独对图片地址发起请求获取图片二进制类型的数据

imagespipeline:

只需要将imgsrc的属性值进行解析,提交到管道,管道就会对图片的src进行请求发送获取图片的二进制类型的数据,且还会帮我们进行持久化操作

案例7.7.1:爬取站长素材中的高清图片

需求:

1. 爬取站长素材中的高清图片

技术:scrapyxpathresponseyield

在此之前需要安装pillow库: 

pip install pillow

思路:

数据解析(图片的地址)

将存储图片的item提交到制定的管道类

pipelines.py管道文件中自定制一个基于imagespipeline的一个管道类

get_media_request()

file_path

item_completed

settings.py配置文件中:

指定图片存储的目录:IMAGES_STORE = './imgs_bobo'

指定开启的管道:自定制的管道类

爬虫文件中:

复制代码
import scrapy
from imgsPro.items import ImgsproItem

class ImgSpider(scrapy.Spider):
    name = 'img'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://sc.chinaz.com/tupian/']

    def parse(self, response):
        div_list = response.xpath('//div[@id="container"]/div')
        for div in div_list:
            #注意:在解析时使用伪属性,在图片的冷加载中,大部分都使用的伪标签
            src = 'https:' + div.xpath('./div/a/img/@src2').extract_first()
            # print(src)
            item = ImgsproItem()
            item['src'] = src

            yield item
复制代码

pipelines.py文件中:

复制代码
from scrapy.pipelines.images import ImagesPipeline
import scrapy

class imgsPileLine(ImagesPipeline):
    #重写父类的三种方法
    #就是可以根据图片地址进行图片数据的请求
    def get_media_requests(self, item, info):

        yield scrapy.Request(item['src'])

    #指定图片存储的路径
    def file_path(self, request, response=None, info=None, *, item=None):
        imgName = request.url.split('/')[-1]  #以/分割,拿到最后一个
        return imgName

    def item_completed(self, results, item, info):
        return item #返回给下一个即将被执行的管道类

setting.py文件中: 除基本操作外
#修改user_agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'

#开启管道类
ITEM_PIPELINES = {
   'imgsPro.pipelines.imgsPileLine': 300,
}

#指定图片存储的目录
IMAGES_STORE = './imgs_bobo' #图片存储的仓库
复制代码

运行程序,得到一个文件夹,里面就是我们拿到的图片:

                                 

                                         29

7.8.中间件

下载中间件:

位置:引擎和下载器之间

作用:批量拦截到整个工程中所有的请求和响应

拦截请求:

UA伪装:process_request

代理IPprocess_exception:return request

案例7.8.1:拦截百度请求

需求:

1. 拦截百度请求

技术:

scrapyxpath、数据存储、下载中间件、拦截请求、ua伪装、代理

爬虫文件中:

复制代码
import scrapy

class MiddleSpider(scrapy.Spider):
    name = 'middle'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['http://www.baidu.com/s?wd=ip']

    def parse(self, response):
        page_text = response.text
        # page_text = response.body  获取二进制属性

        with open('./ip.html','w',encoding='utf-8') as fp:
            fp.write(page_text)
复制代码

middlewares.py文件: 首先删除爬虫中间件,这里只需要用到下载中间件

复制代码
import random
#下载中间件
class MiddleproDownloaderMiddleware:
    user_agents_list = [
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60',
        'Opera/8.0 (Windows NT 5.1; U; en)',
        'Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; en) Opera 9.50',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0',
        'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2 ',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36',
        'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
        'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36',
        'Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
        'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
        'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
        'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0) ',
    ]
    PROXY_http = [
        '183.173.175.46:10080',
        '206.253.164.108:80',
    ]
    PROXY_https = [
        '113.200.56.13:8010',
        '171.221.239.11:808',
    ]

    #拦截请求
    def process_request(self, request, spider):
        #UA伪装
        request.headers['User-Agent'] = random.choice(self.user_agents_list)  #表示从列表中随机选一个

       #为了验证代理的操作是否生效
        request.meta['proxy'] = 'http://223.96.90.216:8085'
        return None
    #拦截所有的响应
    def process_response(self, request, response, spider):        
        return response
    #拦截发生异常的请求
    def process_exception(self, request, exception, spider):
        if request.url.split(':')[0] == 'http':  #通过:进行分隔,拿到第0个
            #代理
            request.meta['proxy'] = 'http://' + random.choice(self.PROXY_http)
        else:
            request.meta['proxy'] = 'https://' + random.choice(self.PROXY_https)

        return request  #将修正之后的请求对象进行重新的请求发送
复制代码

最后得到一个HTML文件:

                                    

30

拦截响应:

篡改响应数据,响应对象

案例7.8.2:爬取网易新闻中的新闻(标题和内容)

需求:

1. 爬取网易云新闻中的新闻

技术:scrapyseleniumxpathyieldjoin

思路: 通过网易云新闻的首页解析出五大板块对应的详情页面的url(没有动态加载) 每一个板块对应额新闻标题都是动态加载出来的(动态加载) 通过解析出每一条新闻详情页的url获取详情页的页面源码,解析出新闻的内容

爬虫文件:

 

复制代码
import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem

class WangyiSpider(scrapy.Spider):
    name = 'wangyi'
    # allowed_domains = ['www.xxx.com']
    start_urls = ['https://news.163.com/']
    models_urls = []  #存储五大板块对应详情页的url
    #解析五大板块对应的详情页的url

    #实例化一个浏览器对象
    def __init__(self):
        self.bro =webdriver.Chrome(executable_path='E:\大数据工作室\比赛\代码\爬虫\scrapy实战\wangyiPro\chromedriver.exe')

    def parse(self, response):
        li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
        alist = [2,3,5,6,7]
        for index in alist:
            model_url = li_list[index].xpath('./a/@href').extract_first()
            self.models_urls.append(model_url)

        #依次对每一个板块所对应的页面进行请求
        for url in self.models_urls:    #对每一个板块的url进行请求发送
            yield scrapy.Request(url,callback=self.parse_model)

    #每一个板块对应的新闻标题相关的内容都是动态加载
    def parse_model(self,response): #解析每一个模块页面对应新闻的标题和详情页面的url‘
        # pass
        div_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div[1]/div/ul/li/div')
        for div in div_list:
            title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
            new_detail_url = div.xpath('./div/div[1]/h3/a').extract_first()

            item = WangyiproItem()
            item['title'] = title
            #对新闻详情页的url发起请求
            yield scrapy.Request(url=new_detail_url,callback=self.parse_detail,meta={'item':item})

    def parse_detail(self,response): #解析新闻内容
        content = response.xpath('//*[@id="content"]/div[2]//text()').extract()
        content = ''.join(content)
        item = response.mata['item']
        item['content'] = content

        yield item

    def closed(self,spider):
        self.bro.close()
复制代码

 

middlewares.py:

复制代码
 #该方法拦截五大板块对应的响应对象,进行篡改
    def process_response(self, request, response, spider):  #spider表示爬虫对象
        bro = spider.bro #获取了在爬虫类中定义的浏览器对象

        #挑选出指定的响应对象进行篡改
        #通过url指定request
        #通过request指定response

        if request.url in spider.models_url:
            bro.get(request.url) #五大板块对应的url进行请求
            sleep(2)
            page_text = bro.page_source  #包含了动态加载的新闻数据
            # response  #五大板块对应的响应对象
            #针对定位到的这些response进行篡改
            #实例化一个新的响应对象(复合需求:包含动态加载出的新闻数据),替代原来旧的响应对象

            #如何获取动态加载出的新闻数据?
                #基于selenium彬姐的获取动态加载数据
            new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
            return new_response
        else:
            # response  #其他板块对应的响应对象
            return response
复制代码

 

7.9.CrawlSpider

spider的一个子类

全栈数据爬取的两种方式:

基于Spider:手动请求

基于CrawlSpider

 

CrawlSpider的使用:

创建一个工程

cd  xxx

创建的爬虫文件(CrawlSpider):

scrapy genspidr -t crawl xxx www.xxx.com

链接提取器(LinkExtractor):

作用:根据指定的规则进行指定连接的提取

规则解析器(Rule):

作用:将连接提取器提取到的连接进行指定规则(callback)的解析操作

7.10.分布式爬虫

概念:我们需要搭建一个分布式的集群,让其对一组资源进行分布式联合爬取。

作用:提升爬取数据的概率

 

如何实现分布式:

安装一个scrapy-redis的主组件

原生的scrapy是不可以实现分布式爬虫,必须要让scrapy结合着scrapy-redis组件一起实现分布式爬虫

为什么原生的scrapy不能实现分布式?

调度器不可以备分布式集群共享

管道不可以被分布式集群共享

scrapy-redis组件作用:

可以给原生的scrapy框架提供可以被共享的管道和调度器

实现流程:

创建工程(fbsPro

创建一个基于CrawlSpider的爬虫文件(fbs)

修改当前的爬虫文件:

导包:from scrapy_redis.spiders import RedisCrawlSpider

start_urlsallowed_domains注释

添加一个新属性:redis_key = 'sun' 可以被共享的调度器队列的名称

编写数据,解析相关的操作

将当前爬虫类的父类修改成RedisCrawlSpider

修改配置文件settings

指定使用可以被共享的管道:

 

复制代码
#添加指定管道
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 400
}

#添加指定调度器
#增加了一个去重容器类的配置,作用使用Redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
#使用scrapy_redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
#配置调度器是否持久化,也就是当爬虫结束,要不要清空Redis中请求队列和去重指纹的set。如果是True,就只爬取没有爬过的数据,爬过的就不考虑
SCHEDULER_PERSIST = True
复制代码

    redis相关操作配置:

      配置redis的配置文件:

        linux或者macredis.conf

        windowsredis.windows.conf

        打开配置文件修改:

          将bind 127.0.0.1进行删除

          关闭保护模式:protected-mode yes 改为no

posted @   LackyQueen  阅读(286)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示