爬虫介绍

爬虫介绍

爬虫是什么?
	-爬虫就是程序---》从互联网中,各个网站上,爬取数据[你能浏览的页面才能爬],做数据清晰
  
爬虫的本质
	-模拟http请求,获取数据---》入库
	-网站
	-app:抓包
  
补充:百度其实就是一个大爬虫
    -百度爬虫一刻不停的在互联网中爬取各个页面---》爬取完后---》保存到自己的数据库中
    -你在百度搜索框中搜索---》百度自己的数据库查询关键字---》返回回来
    -点击某个页面----》跳转到真正的地址上
    -seo:
    -sem:充钱的
   
我们所学习的
	-模拟发送http请求
	-requests模块
 	-selenium
 	-反爬:封ip,解决ip代理,封账号:解决cookie池
       
	-爬虫框架:scrapy

requests模块介绍

使用python如何发送http请求
模块:requests模块,封装了python内置模块urllib
    使用requests可以模拟浏览器的请求(http),比起之前用到的urllib,requests模块的api更加快捷(本质就是封装了urllib3)
   
安装:
    pip3 install requests

request发送get请求

import requests
#
# res = requests.get('https://www.cnblogs.com/liuqingzheng/p/16005866.html')
# print(res.text)


# 如果有的网站,发送请求,不返回数据,人家做了反扒---》拿不到数据,学习如何反扒
# res = requests.get('https://dig.chouti.com/')
# print(res.text)

requests携带参数

import requests

# 方式一:直接拼接到路径中
# res = requests.get('https://www.cnblogs.com/liuqingzheng/p/16005866.html?name=lqz&age=19')
# 方式二:使用params参数
res = requests.get('https://www.cnblogs.com/liuqingzheng/p/16005866.html',params={'name':"lqz",'age':19})
# print(res.text)
print(res.url)

url编码解码

import requests
from urllib.parse import quote,unquote
# res = requests.get('https://www.cnblogs.com/liuqingzheng/p/16005866.html',params={'name':"彭于晏",'age':19})
# # print(res.text)
#
# # 如果是中文,在地址栏中会做url的编码:彭于晏:%E5%BD%AD%E4%BA%8E%E6%99%8F
# print(res.url)
# 'https://www.baidu.com/s?wd=%E5%B8%85%E5%93%A5'


# 编码:
# res=quote('彭于晏')
# print(res)

# 解码
res=unquote('%E5%BD%AD%E4%BA%8E%E6%99%8F')
print(res)

携带请求头

反爬措施之一就是请求头

http请求中,请求头中有一个很重要的参数User-Agent
	-表明了客户端类型是什么:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36
 	-如果没有带这个请求头,后端就禁止
   -requests发送请求,没有携带该参数,所以有的网站就禁止了

import requests
# http请求头:User-Agent,cookie,Connection

# http协议版本间的区别
# Connection: keep-alive
# http协议有版本:主流1.1   0.9   2.x
# http 基于TCP 如果建立一个http链接---》底层创建一个tcp链接
# 1.1比之前多了keep-alive
# 2.x比1.x多了 多路复用
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
res = requests.get('https://dig.chouti.com/',headers=headers)
print(res.text)

发送post请求,携带数据

import requests

# 携带登录信息,携带cookie
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36',
    'Cookie': ''
}
# post请求,携带参数
data = {
    'linkId': '38063872'
}
res = requests.post('https://dig.chouti.com/link/vote', headers=headers, data=data)
print(res.text)



# 双token认证

自动登录,携带cookie的两种方式

# 登录功能,一般都是post

import requests

data = {
    'username': '',
    'password': '',
    'captcha': '3456',
    'remember': 1,
    'ref': 'http://www.aa7a.cn/',
    'act': 'act_login'
}

res = requests.post('http://www.aa7a.cn/user.php',data=data)
print(res.text)
# 响应中会有登录成功的的cookie,
print(res.cookies)  # RequestsCookieJar 跟字典一样
# 拿着这个cookie,发请求,就是登录状态


# 访问首页,get请求,携带cookie,首页返回的数据一定会有 我的账号
# 携带cookie的两种方式  方式一是字符串,方式二是字典或CookieJar对象
# 方式二:放到cookie参数中
res1=requests.get('http://www.aa7a.cn/',cookies=res.cookies)

print('616564099@qq.com' in res1.text)

requests.session的使用

为了保持cookie,以后不需要携带cookie

import requests

data = {
    'username': '',
    'password': '',
    'captcha': '3456',
    'remember': 1,
    'ref': 'http://www.aa7a.cn/',
    'act': 'act_login'
}

session = requests.session()
res = session.post('http://www.aa7a.cn/user.php', data=data)
print(res.text)
res1 = session.get('http://www.aa7a.cn/')  # 自动保持登录状态,自动携带cookie

print('616564099@qq.com' in res1.text)

补充post请求携带数据编码格式

import requests

# data对应字典,这样写,编码方式是urlencoded
requests.post(url='xxxxxxxx',data={'xxx':'yyy'})
# json对应字典,这样写,编码方式是json格式
requests.post(url='xxxxxxxx',json={'xxx':'yyy'})
# 终极方案,编码就是json格式
requests.post(url='',
              data={'':1,},
              headers={
                  'content-type':'application/json'
              })

响应Response对象

# Response相应对象的属性和方法
import requests
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'
}
respone=requests.get('http://www.jianshu.com',headers=headers)
# respone属性
print(respone.text) # 响应体转字符串
print(respone.content) # 响应体的二进制内容

print(respone.status_code) # 响应状态码
print(respone.headers)   # 响应头
print(respone.cookies)  # cookie是在响应头,cookie很重要,它单独做成了一个属性
print(respone.cookies.get_dict()) # cookieJar对象---》转字典
print(respone.cookies.items())  # 一次性取出cooike的键值对

print(respone.url)    # 请求地址
print(respone.history) # 

print(respone.encoding)  # 响应编码格式

编码问题

# 有的网站,打印
res.text --->发现乱码---》请求回来的二进制---》转成了字符串---》默认用utf8转---》
response.encoding='gbk'
再打印res.text它就用gbk转码

下载图片,视频

import requests

# res=requests.get('http://pic.imeitou.com/uploads/allimg/230224/7-230224151210-50.jpg')
# # print(res.content)
# with open('美女.jpg','wb') as f:
#     f.write(res.content)
#


res=requests.get('https://vd3.bdstatic.com/mda-pcdcan8afhy74yuq/sc/cae_h264/1678783682675497768/mda-pcdcan8afhy74yuq.mp4')
with open('致命诱惑.mp4','wb') as f:
    for line in res.iter_content():
        f.write(line)

requests高级用法

解析json

# 发送http请求,返回的数据会有xml格式,也有json格式
import requests
data = {
    'cname': '',
    'pid': '',
    'keyword': '500',
    'pageIndex': 1,
    'pageSize': 10,
}
res = requests.post('http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=keyword',data=data)
# print(res.text)  # json 格式字符串---》json.cn
print(type(res.json()))  # 转成对象  字典对象

ssl认证

http协议:明文传输
https:http+ssl/tls
  HTTP+SSL/TLS 也就是在http上又加了一层处理加密信息的模块,比http安全,可防止组织在传输过程中被窃取,改变,确保数据的完整性
	https://zhuanlan.zhihu.com/p/561907474
  
以后遇到整数提示错误问题,ssl xxx
1.不验证证书
import requests
response = requests.get('https://www.12306.cn',verify=False)
# 参数:verify就是表示是否验证证书,不验证证书,报警警告,返回200
print(response.status_code)

2.关闭警告
import requests
from requests.packages import urllib3
urllib3.disable_warnings()  # 关闭警告
response = requests.get('https://www.12306.cn',verify=False)
# 这里就不会有警告了
print(response.status_code)

3 手动携带证书(了解)
    import requests
    respone=requests.get('https://www.12306.cn',
                         cert=('/path/server.crt',
                               '/path/key'))
    print(respone.status_code)

使用代理

如果爬虫使用自身ip地址访问,很有可能被封ip地址,以后就访问不了了

解决办法:我们可以使用代理ip
代理:收费和免费(不稳,延迟高)
 
# res = requests.post('https://www.cnblogs.com',proxies={'http':'地址+端口'})

# res = requests.post('https://www.cnblogs.com',proxies={'http':'27.79.236.66:4001'})
res = requests.post('https://www.cnblogs.com',proxies={'http':'60.167.91.34:33080'})
print(res.status_code)

# 高匿代理和透明代理
	-高匿:服务端拿不到真实客户端的ip地址
 	-透明:服务端能拿到真实客户端的ip地址
     
   -后端如何拿到真实客户端ip地址
		-http请求中有个:X-Forwarded-For: client1, proxy1, proxy2, proxy3
        -x-forword-for
        -获得HTTP请求端真实的IP

超时设置

import requests
respone=requests.get('https://www.baidu.com',timeout=0.0001)

异常处理

import requests
from requests.exceptions import * #可以查看requests.exceptions获取异常类型

try:
    r=requests.get('http://www.baidu.com',timeout=0.00001)
except ReadTimeout:
    print('===:')
# except ConnectionError: #网络不通
#     print('-----')
# except Timeout:
#     print('aaaaa')

except RequestException:
    print('Error')

上传文件

import requests
files = {'file': open('美女.png', 'rb')}
respone = requests.post('http://httpbin.org/post', files=files)
print(respone.status_code)

代理池搭建

requests发送请求使用代理
代理从哪来:
    -公司花钱买
 	 -搭建免费的代理池:https://github.com/jhao104/proxy_pool
    	-python:爬虫+flask写的
       -架构:看下图
     
# 搭建步骤:
	1 git clone https://github.com/jhao104/proxy_pool.git
    2 使用pycharm打开
    3 安装依赖:pip install -r requirements.txt
    4 修改配置文件(redis地址即可)
        HOST = "0.0.0.0"
        PORT = 5010
        DB_CONN = 'redis://127.0.0.1:6379/0'
        PROXY_FETCHER #爬取哪些免费代理网站
   	5 启动爬虫程序
    python proxyPool.py schedule
	6 启动服务端
    python proxyPool.py server
    
    7 使用随机一个免费代理
    地址栏中输入:http://127.0.0.1:5010/get/
  
# 使用随机代理发送请求
import requests
from requests.packages import urllib3

urllib3.disable_warnings()  # 关闭警告
# 读取代理
res = requests.get('http://127.0.0.1:5010/get/').json()
# print(res)
# {'anonymous': '', 'check_count': 2, 'fail_count': 0, 'https': False, 'last_status': True,
# 'last_time': '2023-03-16 16:21:50',
# 'proxy': '121.33.160.52:1080', 'region': '中国 广东 广州 电信', 'source': 'freeProxy09'}
proxies = {}
if res['https']:  # 判断代理是是https,还是http
    proxies['https'] = res['proxy']  # 将代理池的代理放到我们的字典中
else:
    proxies['http'] = res['proxy']
print(proxies)  # 随机获取代理地址{'http': '58.17.78.91:8085'}
res = requests.post('https://www.cnblogs.com', proxies=proxies, verify=False)
print(res)  # <Response [200]> 响应成功

image

django后端获取客户端的ip地址

# 建立django后端---》index地址---》访问就返回访问者的ip


# django代码---》不要忘记改配置文件
# 路由
path('', index),
# 视图函数
def index(request):
    ip = request.META.get('REMOTE_ADDR')
    print('ip地址是', ip)
    return HttpResponse(ip)



# 测试端:

# import requests
# from requests.packages import urllib3
# urllib3.disable_warnings() #关闭警告
# # 获取代理
# res = requests.get('http://127.0.0.1:5010/get/').json()
# proxies = {}
# if res['https']:
#     proxies['https'] = res['proxy']
# else:
#     proxies['http'] = res['proxy']
#
# print(proxies)
# res=requests.get('http://101.43.19.239/', proxies=proxies,verify=False)
# print(res.text)


from threading import Thread
import requests


def task():
    res = requests.get('http://101.43.19.239/')
    print(res.text)


for i in range(10000000):
    t = Thread(target=task)
    t.start()

爬取某视频网站

import requests
import re

res = requests.get('https://www.pearvideo.com/category_loading.jsp?reqType=5&categoryId=1&start=0')
# print(res.text)  # 拿到整体网页
video_list = re.findall('<a href="(.*?)" class="vervideo-lilink actplay">', res.text)
# print(video_list)  # ['video_1346772', 'video_1433296', 'video_1420573', 'video_1397663']
for i in video_list:
    # print(i)  # video_1346772
    video_id = i.split('_')[1]
    # print(video_id)  # 1346772
    real_url = 'https://www.pearvideo.com/' + i
    headers = {
        'Referer': 'https://www.pearvideo.com/video_%s' % video_id
    }
    res1 = requests.get('https://www.pearvideo.com/videoStatus.jsp?contId=%s&mrd=0.29636538326105044' % video_id,
                        headers=headers).json()
    # print(res1["videoInfo"]['videos']['srcUrl'])
    mp4_url = res1["videoInfo"]['videos']['srcUrl']
    mp4_url = mp4_url.replace(mp4_url.split('/')[-1].split('-')[0], 'cont-%s' % video_id)
    print(mp4_url)
    res2 = requests.get(mp4_url)
    with open('./video/%s.mp4' % video_id, 'wb') as f:
        for line in res2.iter_content():
            f.write(line)
  
# headers={
#     'Referer': 'https://www.pearvideo.com/video_1212452'
# }
# res=requests.get('https://www.pearvideo.com/videoStatus.jsp?contId=1212452&mrd=0.29636538326105044',headers=headers)
#
# print(res.text)


# https://video.pearvideo.com/mp4/short/20171204/    1678938313577    -11212458-hd.mp4
# https://video.pearvideo.com/mp4/short/20171204/     cont-1212452    -11212458-hd.mp4

mp4_url = 'https://video.pearvideo.com/mp4/short/20171204/  1678938313577-11212458-hd.mp4'

爬取新闻

import requests
from bs4 import BeautifulSoup

res = requests.get('https://www.autohome.com.cn/all/1/#liststart')
# print(res.text)
# 第一个参数是要解析的文本str
# 第二个参数是:解析的解析器,html,parser:内置解析器
soup = BeautifulSoup(res.text, 'html.parser')
# 查找所有类型叫article的ul标签,find_all
ul_list = soup.find_all(name='ul', class_='article')
print(ul_list)
for ul in ul_list:
    li_list = ul.find_all(name='li')
    print(len(li_list))
    for li in li_list:
        h3 = li.find(name='h3')
        # print(h3)
        if h3:
            title = h3.text
            url = 'https' + li.find('a').attrs['href']
            desc = li.find('p').text
            img = li.find(name='img').attrs['src']
            print('''
                        新闻标题:%s
                        新闻连接:%s
                        新闻摘要:%s
                        新闻图片:%s
            ''' % (title, url, desc, img))

bs4介绍,遍历文档树

beautifulsoup4从HTML或XML文件中提取数据的Python库

用它来解析爬取回来的xml
安装:pip install beautifulsoup4
    pip install lxml  # 这是下载解析库

soup=BeautifulSoup('要解析的内容str类型','html.parser/lxml'

bs4的遍历文档树

from bs4 import BeautifulSoup
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title">
lqz
<b>The Dormouse's story</b>
</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1" name='lqz'>Elsie</a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

soup = BeautifulSoup(html_doc, 'lxml')
# 1.美化,不是标准xml,完成美化
# print(soup.prettify())

# 2.遍历文档树---》通过.来遍历
# print(soup.html.body.p)  # html中body中第一个p标签,一层一层找,只会找一个
# print(soup.p)  # 跨层找,也是只找第一个

# 3.获取标签的属性---》属性字典
# print(soup.a.attrs)  # 获取a标签下的所有属性
# {'href': 'http://example.com/elsie', 'class': ['sister'], 'id': 'link1', 'name': 'lqz'}
# 拿到属性就可以索引或者get取值
# print(soup.a.attrs['href'])  # 拿到属性href对应的链接

# 4.获取标签的内容
# text 获得该标签内部子子孙孙所有标签的文本内容
# print(soup.p.text)  # 拿到第一个p标签文本内容包含他的子标签b标签的文本内容
# # string p下的文本只有一个时,取到,否则为None
# print(soup.p.string)
# # strings
# print(list(soup.p.strings)) # generator

# 5.嵌套选择
# print(soup.html.body)

# ---- 了解
#6、子节点、子孙节点
# print(soup.body.contents) #p下所有子节点,只取一层
# print(list(soup.p.children)) #list_iterator得到一个迭代器,包含p下所有子节点  只取一层
# print(list(soup.body.descendants) ) # generator  子子孙孙
#7、父节点、祖先节点

# print(soup.a.parent) #获取a标签的父节点  直接父亲
# print(list(soup.a.parents) )#找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...
#8、兄弟节点
# print(soup.a.next_sibling) #下一个兄弟
# print(soup.a.previous_sibling) #上一个兄弟
#
# print(list(soup.a.next_siblings)) #下面的兄弟们=>生成器对象
print(list(soup.a.previous_siblings)) #上面的兄弟们=>生成器对象

bs4搜索文档树

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>
</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""
from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc, 'lxml')
# 五种过滤器:字符串、正则表达式、列表、True、方法,这些都指的是查询条件

# 1.字符串
# res = soup.find_all(name='p')  # 查询所有的p标签
# print(res)

# 类名交sister的所有标签
# res = soup.find_all(class_='sister')
# print(res)

# id叫link1的标签
# res = soup.find_all(id='link1')
# print(res)

# 文本内容叫Elsie的父标签
# res = soup.find(text='Elsie').parent
# print(res)
# 另一种方式
# res = soup.find_all(attrs={'class':'sister'})
# res = soup.find_all(attrs={'id':'link1'})

# 2.正则表达式
# import re
# # res=soup.find_all(id=re.compile('^l'))
# res=soup.find_all(class_=re.compile('^s'))
# print(res)

# 3.列表
# 查询id为link1的标签和id为link2的标签
# res = soup.find_all(id=['link1','link2'])
# print(res)
# print(soup.find_all(name=['a','b']))
# print(soup.find_all(['a','b']))

# 4 True
# res=soup.find_all(id=True)  # 所有有id的标签
# res=soup.find_all(href=True)
# res=soup.find_all(class_=True)
# print(res)

# 5 方法
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(name=has_class_but_no_id))

find的其他参数

find的参数有:
    name
    class_
    id
    text
    attrs
    -----
    limit:限制条数,find_all用的,find的本质是find_all,limit=1
    recursive:查找的时候,是只找第一层还是子子孙孙都找,默认是True,子子孙孙都找
  
# limit 参数
# res=soup.find_all(href=True,limit=2)
# print(res)

# recursive  查找的时候,是只找第一层还是子子孙孙都找
# res=soup.find_all(name='b',recursive=False)
# res=soup.find_all(name='b')

# 建议遍历和搜索一起用
res=soup.html.body.p.find_all(name='b',recursive=False)
print(res)

CSS选择器

之前学过的css选择器,可以很复杂
	.类名
    # id
    p
 
学了bs4,以后可能见到别的解析器(lxml)----》他们都会支持css选择器,也会支持xpath
# bs4 支持css选择器

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title">
    <b>The Dormouse's story</b>
    Once upon a time there were three little sisters; and their names were
    <a href="http://example.com/elsie" class="sister" id="link1">
        <span>Elsie</span>
    </a>
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    <div class='panel-1'>
        <ul class='list' id='list-1'>
            <li class='element'>Foo</li>
            <li class='element'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
        <ul class='list list-small' id='list-2'>
            <li class='element'><h1 class='yyyy'>Foo</h1></li>
            <li class='element xxx'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
    </div>
    and they lived at the bottom of a well.
</p>
<p class="story">...</p>
"""

from bs4 import BeautifulSoup

soup = BeautifulSoup(html_doc, 'lxml')

# select内写css选择器
# 查到类名是sister的标签
# print(soup.select('.sister'))
# 查找id是link1的标签
# print(soup.select('#link1'))
# 查找id是link1里面的span标签
# print(soup.select('#link1 span'))
# 终极大招---》如果不会写css选择器,可以复制

import requests
res=requests.get('https://www.w3school.com.cn/css/css_selector_attribute.asp')
soup=BeautifulSoup(res.text,'lxml')
# print(soup.select('#intro > p:nth-child(1) > strong'))
print(soup.select('#intro > p:nth-child(1) > strong')[0].text)

selenium基本使用

selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题

selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

使用步骤:
    1.下载selenium
    2.操作浏览器,分不同浏览器,需要下载不同浏览器的驱动
    -用谷歌---》谷歌浏览器驱动:https://registry.npmmirror.com/binary.html?path=chromedriver/
        -跟谷歌浏览器版本要对应  111.0.5563.65:
		
        
    3 下载完的驱动,放在项目路径下
    
    4 写代码,控制谷歌浏览器
    from selenium import webdriver
    import time
    bro = webdriver.Chrome(executable_path='chromedriver.exe')  # 打开一个谷歌浏览器
    bro.get('https://www.baidu.com/s?wd=%E7%BE%8E%E5%A5%B3')  # 在地址栏中输入地址
    print(bro.page_source)  # 当前页面的内容  (html格式)
    with open('1.html','w',encoding='utf-8') as f:
        f.write(bro.page_source)
    time.sleep(5)
    bro.close()  # 关闭浏览器

无界面浏览器

from selenium import webdriver
import time
from selenium.webdriver.chrome.options import Options
# 隐藏浏览器的图形化界面,但是数据还拿到
chrome_options = Options()
chrome_options.add_argument('window-size=1920x3000') #指定浏览器分辨率
chrome_options.add_argument('--hide-scrollbars') #隐藏滚动条, 应对一些特殊页面
chrome_options.add_argument('blink-settings=imagesEnabled=false') #不加载图片, 提升速度
chrome_options.add_argument('--headless') #浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
# chrome_options.binary_location = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" #手动指定使用的浏览器位置
bro = webdriver.Chrome(executable_path='chromedriver.exe',chrome_options=chrome_options)
# 打开一个谷歌浏览器,隐藏浏览器的图形化界面,但是数据拿到了
bro.get('https://www.cnblogs.com/')
print(bro.page_source)
time.sleep(5)
bro.close()  # 关闭浏览器

模拟登录百度

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

bro = webdriver.Chrome(executable_path='chromedriver.exe')  # 打开一个谷歌浏览器
bro.get('https://www.baidu.com')
# 加入等待:找标签,如果找不到,就等待 x秒,如果还找不到就报错
bro.implicitly_wait(10)  # 1 等待
# 从页面中找到登录 a标签,点击它
# By.LINK_TEXT  按a标签文本内容找
btn = bro.find_element(by=By.LINK_TEXT, value='登录')
# 点击它
btn.click()

# 找到按账号登录的点击按钮,有id,优先用id,因为唯一  TANGRAM__PSP_11__changePwdCodeItem
btn_2 = bro.find_element(by=By.ID, value='TANGRAM__PSP_11__changeSmsCodeItem')
btn_2.click()
time.sleep(1)

btn_2 = bro.find_element(by=By.ID, value='TANGRAM__PSP_11__changePwdCodeItem')
btn_2.click()
time.sleep(1)

name = bro.find_element(by=By.ID, value='TANGRAM__PSP_11__userName')
password = bro.find_element(by=By.ID, value='TANGRAM__PSP_11__password')
name.send_keys('306334678@qq.com')
password.send_keys('1234')
time.sleep(1)

submit=bro.find_element(by=By.ID,value='TANGRAM__PSP_11__submit')
submit.click()
time.sleep(2)
bro.close()  # 关闭浏览器

selenium其它用法

查找标签

# 两个方法
bro.find_element 找一个
bro.find_elements 找所有

# 可以按id,标签名,name属性名,类名,a标签的文字,a标签的文字模糊匹配,css选择器,xpath【后面聊】
# input_1=bro.find_element(by=By.ID,value='wd')  # 按id找
# input_1 = bro.find_element(by=By.NAME, value='wd')  # name属性名
# input_1=bro.find_element(by=By.TAG_NAME,value='input') # 可以按标签名字找
# input_1=bro.find_element(by=By.CLASS_NAME,value='s_ipt') # 可以按类名
# input_1=bro.find_element(by=By.LINK_TEXT,value='登录') # 可以按a标签内容找
# input_1=bro.find_element(by=By.PARTIAL_LINK_TEXT,value='录') # 可以按a标签内容找
# input_1 = bro.find_element(by=By.CSS_SELECTOR, value='#su')  # 可以按css选择器

获取位置属性大小,文本

print(tag.get_attribute('src'))  # 用的最多
tag.text  # 文本内容
#获取标签ID,位置,名称,大小(了解)
print(tag.id)
print(tag.location)
print(tag.tag_name)
print(tag.size)

等待元素被加载

# 代码执行很快,有的标签没来的及加载,直接查找就会报错,设置等待
# 隐士等待:所有标签,只要去找,找不到就遵循 等10s的规则
	bro.implicitly_wait(10)
# 显示等待:需要给每个标签绑定一个等待,麻烦

元素操作

# 点击
tag.click()

# 输入内容
tag.send_keys()

# 清空内容
tag.clear()


# 浏览器对象 最大化
bro.maximize_window() 
#浏览器对象  截全屏
bro.save_screenshot('main.png') 

执行js代码

bro.execute_script('alert("美女")')  # 引号内部的相当于 用script标签包裹了


# 可以干的事
	-获取当前访问的地址  window.location
    -打开新的标签
    -滑动屏幕--》bro.execute_script('scrollTo(0,document.documentElement.scrollHeight)')
    -获取cookie,获取定义的全局变量

切换选项卡

import time
from selenium import webdriver

browser=webdriver.Chrome(executable_path='chromedriver.exe')
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')

print(browser.window_handles) #获取所有的选项卡


browser.switch_to.window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(2)
browser.switch_to.window(browser.window_handles[0])
browser.get('https://www.sina.com.cn')
browser.close()

浏览器前进后退

import time
from selenium import webdriver

browser=webdriver.Chrome(executable_path='chromedriver.exe')
browser.get('https://www.baidu.com')
browser.get('https://www.taobao.com')
browser.get('http://www.sina.com.cn/')

browser.back()
time.sleep(2)
browser.forward()
browser.close()

异常处理

import time
from selenium import webdriver

browser=webdriver.Chrome(executable_path='chromedriver.exe')
try:
except Exception as e:
    print(e)
    
finally:
    browser.close()

0 selenium 登录cnblogs

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import json

bro = webdriver.Chrome(executable_path='./chromedriver.exe')

try:
    ####1 获取cookie
    # bro.get('https://www.cnblogs.com/')
    # bro.implicitly_wait(10)
    # login_btn = bro.find_element(by=By.LINK_TEXT, value='登录')
    # login_btn.click()
    # username = bro.find_element(By.ID, 'mat-input-0')
    # password = bro.find_element(By.ID, 'mat-input-1')
    # submit_btn = bro.find_element(By.CSS_SELECTOR,
    #                               'body > app-root > app-sign-in-layout > div > div > app-sign-in > app-content-container > div > div > div > form > div > button')
    # username.send_keys('616564099@qq.com')
    # # 手动输入密码,手动点击登录 搞好验证码,都成功,敲回车
    # input()
    #
    # # 取出cookies
    # cookie = bro.get_cookies()
    # print(cookie)
    # # 保存到本地文件
    # with open('cnblogs.json', 'w', encoding='utf-8') as f:
    #     json.dump(cookie, f)

    ### 2 打开首页
    bro.get('https://www.cnblogs.com/')  # 没有登录状态
    bro.implicitly_wait(10)
    time.sleep(2)
    # 打开本地的cookie的json文件
    with open('cnblogs.json', 'r', encoding='utf-8') as f:
        cookies = json.load(f)
    for cookie in cookies:
        bro.add_cookie(cookie)

    bro.refresh()  # 刷新

    time.sleep(5)

except Exception as e:
    print(e)

finally:
    bro.close()

1 抽屉半自动点赞

# 1 使用selenium 半自动登录---》取到cookie
# 2 使用requests模块,解析出点赞的请求地址---》模拟发送请求---》携带cookie
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
import json
import requests

# bro = webdriver.Chrome(executable_path='./chromedriver.exe')

try:

    ####1 先登录,获取cookie
    # bro.get('https://dig.chouti.com/')
    # bro.maximize_window()
    # bro.implicitly_wait(10)
    #
    # login_btn = bro.find_element(By.LINK_TEXT, '登录')
    # # login_btn.click() # 会报错,不能这么点击了
    #
    # # 使用js点击它,把传入的对象,点击一下
    # bro.execute_script("arguments[0].click()", login_btn)
    # time.sleep(3)
    #
    # username = bro.find_element(By.CSS_SELECTOR,
    #                             'body > div.login-dialog.dialog.animated2.scaleIn > div > div.login-body > div.form-item.login-item.clearfix.phone-item.mt24 > div.input-item.input-item-short.left.clearfix > input')
    # password = bro.find_element(By.NAME, 'password')
    #
    # username.send_keys('18953675221')
    # password.send_keys('lqz123')
    # time.sleep(1)
    # submit_btn = bro.find_element(By.CSS_SELECTOR,
    #                               'body > div.login-dialog.dialog.animated2.scaleIn > div > div.login-footer > div:nth-child(4) > button')
    # submit_btn.click()
    #
    # input('')  # 万一有验证码,手动操作一下
    # with open('chouti.json', 'w', encoding='utf-8') as f:
    #     json.dump(bro.get_cookies(), f)

    ### 使用request模拟点赞,携带cookie
    # 先把cookie打开
    with open('chouti.json', 'r', encoding='utf-8') as f:
        cookies = json.load(f)

    # selenium 的cookie不能直接给requests模块使用,需要额外处理一下

    request_cookies = {}
    for cookie in cookies:
        request_cookies[cookie['name']] = cookie['value']

    print(request_cookies)
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'
    }
    res = requests.get('https://dig.chouti.com/top/24hr?_=1679277434856', headers=headers)

    for item in res.json().get('data'):
        id_link = item.get('id')
        data = {
            'linkId': id_link
        }
        res2 = requests.post('https://dig.chouti.com/link/vote', headers=headers, data=data, cookies=request_cookies)
        print(res2.text)

except Exception as e:
    print(e)

finally:
    # bro.close()
    pass

2 xpath的使用

# 每个解析器,都会有自己的查找方法
	-bs4  find 和find_all
    -selenium  find_element和 find_elements 
    -lxml 也是个解析器,支持xpath和css
# 这些解析器,基本上都会支持两种统一的 css和xpath
	-css咱们会了
    -xpath需要学习
    
    
    
# xpath是什么?
	XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言
    
    
# 有终极大招,只需要记住几个用法就可以了
	- /     从当前路径下开始找 
    - /div  从当前路径下开始找div
    -//     递归查找,子子孙孙
    -//div  递归查找div
    -@      取属性
    - .    当成
    - ..   上一层
    
    

3 selenium 动作链

# 人可以滑动某些标签

# 网站中有些按住鼠标,滑动的效果
	-滑动验证码
    
    
# 两种形式
	-形式一:
        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']
        track=0
        while track < distance:
            ActionChains(bro).move_by_offset(xoffset=2,yoffset=0).perform()
            track+=2

3.1 动作链案例

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By  # 按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys  # 键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait  # 等待页面加载某些元素

try:
    browser = webdriver.Chrome(executable_path='./chromedriver.exe')
    browser.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')

    browser.switch_to.frame('iframeResult')  # 切换到id为iframeResult的frame

    target = browser.find_element(By.ID, 'droppable')  # 目标
    source = browser.find_element(By.ID, 'draggable')  # 源

    # 方案1
    # actions = ActionChains(browser)  # 拿到动作链对象
    # actions.drag_and_drop(source, target)  # 把动作放到动作链中,准备串行执行
    # actions.perform()



    # 方案2
    # ActionChains(browser).click_and_hold(source).perform()
    # distance = target.location['x'] - source.location['x']
    # track = 0
    # while track < distance:
    #     ActionChains(browser).move_by_offset(xoffset=2, yoffset=0).perform()
    #     track += 2

    time.sleep(2)





finally:
    browser.close()

4 自动登录12306

# selenium自动登录12306

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By  # 按照什么方式查找,By.ID,By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys  # 键盘按键操作
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait  # 等待页面加载某些元素
from selenium.webdriver.chrome.options import Options

try:
    options = Options()
    options.add_argument("--disable-blink-features=AutomationControlled")  # 去掉自动化控制
    browser = webdriver.Chrome(executable_path='./chromedriver.exe', chrome_options=options)
    browser.get('https://kyfw.12306.cn/otn/resources/login.html')
    browser.maximize_window()
    username = browser.find_element(By.ID, 'J-userName')
    password = browser.find_element(By.ID, 'J-password')
    username.send_keys('')
    password.send_keys('')
    login_btn = browser.find_element(By.ID, 'J-login')

    time.sleep(2)
    login_btn.click()
    time.sleep(5)

    span = browser.find_element(By.ID, 'nc_1_n1z')
    ActionChains(browser).click_and_hold(span).perform()
    ActionChains(browser).move_by_offset(xoffset=300, yoffset=0).perform()

    # 滑动完成了,但是进不去,原因是它检测到我们使用了selenium,屏蔽掉
    time.sleep(3)

finally:
    browser.close()

5 打码平台使用

# 登录网站,会有些验证码,可以借助于第三方的打码平台,破解验证码,只需要花钱解决

# 免费的:纯数字,纯字母的----》python有免费模块破解,失败率不高
# 云打码,超级鹰(以它为例)

# 云打码:https://zhuce.jfbym.com/price/


# 价格体系:破解什么验证码,需要多少钱
	http://www.chaojiying.com/price.html

6 使用打码平台自动登录


# 使用selenium打开页面---》截取整个屏幕----》使用pillow---》根据验证码图片位置,截取出验证码图片---》使用第三方打码平台破解---》写入到验证码框中,点击登录
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()

7 使用selenium爬取京东商品信息

import time

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys  # 键盘按键操作


def get_goods(bro):
    # 找到所有类名叫gl-item 的li标签
    li_list = bro.find_elements(By.CLASS_NAME, 'gl-item')
    for li in li_list:
        try:
            img_url = li.find_element(By.CSS_SELECTOR, '.p-img img').get_attribute('src')
            if not img_url:
                img_url = 'https:' + li.find_element(By.CSS_SELECTOR, '.p-img img').get_attribute('data-lazy-img')
            price = li.find_element(By.CSS_SELECTOR, '.p-price i').text
            name = li.find_element(By.CSS_SELECTOR, '.p-name a').text
            url = 'https:' + li.find_element(By.CSS_SELECTOR, '.p-img a').get_attribute('href')
            commit = li.find_element(By.CSS_SELECTOR, '.p-commit a').text
            print('''
            商品图片地址:%s
            商品地址:%s
            商品名字:%s
            商品价格:%s
            商品评论数:%s
            ''' % (img_url, url, name, price, commit))
        except Exception as e:
            print(e)
            continue

    # 查找下一页,点击,在执行get_goods
    next = bro.find_element(By.PARTIAL_LINK_TEXT, '下一页')
    time.sleep(1)
    next.click()
    get_goods(bro)


try:
    bro = webdriver.Chrome(executable_path='./chromedriver.exe')
    bro.get('http://www.jd.com')
    bro.implicitly_wait(10)

    input_key = bro.find_element(By.ID, 'key')
    input_key.send_keys('茅台')
    input_key.send_keys(Keys.ENTER)  # 敲回车
    # 滑动屏幕到最底部
    bro.execute_script('scrollTo(0,5000)')
    get_goods(bro)



except Exception as e:
    print('sasdfsadfasdfa',e)
finally:
    bro.close()

8 scrapy介绍

# requsets  bs4 selenium  模块
 
# 框架 :django ,scrapy--->专门做爬虫的框架,爬虫界的django,大而全,爬虫有的东西,它都自带


# 安装 (win看人品,linux,mac一点问题没有)
	-pip3.8 install  scrapy
    
    -装不上,基本上是因为twisted装不了,单独装
        1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
        3、pip3 install lxml
        4、pip3 install pyopenssl
        5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
        6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
        7、执行pip3 install 下载目录\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
        8、pip3 install scrapy
        
        
        
# 架构分析
	-爬虫:spiders(自己定义的,可以有很多),定义爬取的地址,解析规则
	-引擎:engine ---》控制整个框架数据的流动,大总管
    -调度器:scheduler---》要爬取的 requests对象,放在里面,排队
    -下载中间件:DownloaderMiddleware---》处理请求对象,处理响应对象
    -下载器:Downloader ----》负责真正的下载,效率很高,基于twisted的高并发的模型之上
    
    -爬虫中间件:spiderMiddleware----》处于engine和爬虫直接的(用的少)
    -管道:piplines---》负责存储数据
    
    
    
    
# 创建出scrapy项目
	scrapy startproject firstscrapy  # 创建项目
    scrapy genspider 名字 网址        # 创建爬虫   等同于 创建app
    
    # pycharm打开

Scrapy框架架构图

0 scrapy架构介绍

img

# 引擎(EGINE)
引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件。

# 调度器(SCHEDULER)
用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址

# 下载器(DOWLOADER)
用于下载网页内容, 并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的

# 爬虫(SPIDERS)--->在这里写代码
SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求

# 项目管道(ITEM PIPLINES)
在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作

# 下载器中间件(Downloader Middlewares)

位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应response,你可用该中间件做以下几件事:设置请求头,设置cookie,使用代理,集成selenium

# 爬虫中间件(Spider Middlewares)
位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)

0.1 scrapy的一些命令

# 安装完成后,会有scrapy的可执行文件

# 创建项目
	scrapy startproject 项目名字  # 跟创建django一样,pycharm能直接创建django
    
    
# 创建爬虫
	scrapy genspider 名字 域名   # 创建爬虫  django的创建app
    
# pycharm打开scrapy的项目


# 运行爬虫
	scrapy crawl 爬虫名字
    
# 想点击绿色箭头运行爬虫
	新建一个run.py,写入,以后右键执行即可
    from scrapy.cmdline import execute
    execute(['scrapy','crawl','cnblogs','--nolog'])
    
    
    
# 补充:爬虫协议
 http://www.cnblogs.com/robots.txt

0.2 scrapy项目目录结构

firstscrapy 						# 项目名
    firstscrapy            # 文件夹名字,核心代码,都在这里面
    	spiders            # 爬虫的文件,里面有所有的爬虫
        	__init__.py
        	baidu.py      # 百度爬虫 
        	cnblogs.py    #cnblogs爬虫
        items.py # 有很多模型类---》以后存储的数据,都做成模型类的对象,等同于django的models.py
        middlewares.py # 中间件:爬虫中间件,下载中间件都写在这里面
        pipelines.py   #项目管道---》以后写持久化,都在这里面写
        run.py         # 自己写的,运行爬虫
        settings.py    # 配置文件  django的配置文件
   scrapy.cfg          # 项目上线用的,不需要关注

1 scrapy解析数据

1 response对象有css方法和xpath方法
	-css中写css选择器
    -xpath中写xpath选择
2 重点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)'
3 重点2:
	.extract_first()  取一个
    .extract()        取所有
    

解析cnblosg

    # def parse(self, response):
    #     # 解析数据 css解析
    #     article_list = response.css('article.post-item')
    #     for article in article_list:
    #         title = article.css('div.post-item-text>a::text').extract_first()
    #         author_img = article.css('div.post-item-text img::attr(src)').extract_first()
    #         author_name = article.css('footer span::text').extract_first()
    #         desc_old = article.css('p.post-item-summary::text').extract()
    #         desc = desc_old[0].replace('\n', '').replace(' ', '')
    #         if not desc:
    #             desc = desc_old[1].replace('\n', '').replace(' ', '')
    #         url=article.css('div.post-item-text>a::attr(href)').extract_first()
    #         # 文章真正的内容,没拿到,它不在这个页面中,它在下一个页面中
    #         print(title)
    #         print(author_img)
    #         print(author_name)
    #         print(desc)
    #         print(url)

    def parse(self, response):
        # 解析数据 css解析
        article_list = response.xpath('//*[@id="post_list"]/article')
        # article_list = response.xpath('//article[contains(@class,"post-item")]')
        for article in article_list:
            title = article.xpath('.//div/a/text()').extract_first()
            author_img = article.xpath('.//div//img/@src').extract_first()
            author_name = article.xpath('.//footer//span/text()').extract_first()
            desc_old = article.xpath('.//p/text()').extract()
            desc = desc_old[0].replace('\n', '').replace(' ', '')
            if not desc:
                desc = desc_old[1].replace('\n', '').replace(' ', '')
            url = article.xpath('.//div/a/@href').extract_first()
            # 文章真正的内容,没拿到,它不在这个页面中,它在下一个页面中
            print(title)
            print(author_img)
            print(author_name)
            print(desc)
            print(url)

2 settings相关配置,提高爬取效率

# scrapy 项目有项目自己的配置文件,还有内置的

2.1 基础的一些

#1 了解
BOT_NAME = "firstscrapy"  #项目名字,整个爬虫名字
#2 爬虫存放位置    了解
SPIDER_MODULES = ["firstscrapy.spiders"]
NEWSPIDER_MODULE = "firstscrapy.spiders"

#3  记住 是否遵循爬虫协议,一般都设为False
ROBOTSTXT_OBEY = False
# 4 记住
USER_AGENT = "firstscrapy (+http://www.yourdomain.com)"
#5  记住  日志级别
LOG_LEVEL='ERROR'

#6   记住 DEFAULT_REQUEST_HEADERS 默认请求头
DEFAULT_REQUEST_HEADERS = {
   'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
   'Accept-Language': 'en',
}

#7 记住 后面学  SPIDER_MIDDLEWARES 爬虫中间件
SPIDER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsSpiderMiddleware': 543,
}
#8 后面学 DOWNLOADER_MIDDLEWARES  下载中间件
DOWNLOADER_MIDDLEWARES = {
    'cnblogs.middlewares.CnblogsDownloaderMiddleware': 543,
}

#9 后面学 ITEM_PIPELINES 持久化配置
ITEM_PIPELINES = {
    'cnblogs.pipelines.CnblogsPipeline': 300,
}

2.2 增加爬虫的爬取效率

#1 增加并发:默认16
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改
CONCURRENT_REQUESTS = 100
值为100,并发设置成了为100。
#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:
LOG_LEVEL = 'INFO'
# 3 禁止cookie:
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:
COOKIES_ENABLED = False
# 4 禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:
RETRY_ENABLED = False
# 5 减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:
DOWNLOAD_TIMEOUT = 10 超时时间为10s

3 持久化方案

# 方式一:(parse必须有return值,必须是列表套字典形式--->使用命令,可以保存到json格式中,csv中。。。)
scrapy crawl cnblogs -o cnbogs.json

# 方式二:我们用的,使用pipline存储---》可以存到多个位置
	-第一步:在item.py中写一个类
        class FirstscrapyItem(scrapy.Item):
            title = scrapy.Field()
            author_img = scrapy.Field()
            author_name = scrapy.Field()
            desc = scrapy.Field()
            url = scrapy.Field()
            # 博客文章内容,但是暂时没有
            content = scrapy.Field()
            
    -第二步:在pipline.py中写代码,写一个类:open_spide,close_spider,process_item
    	-open_spide:开启爬虫会触发
    	-close_spider:爬完会触发
        -process_ite:每次要保存一个对象会触发
        class FirstscrapyFilePipeline:
            def open_spider(self, spider):
                print('我开了')
                self.f=open('a.txt','w',encoding='utf-8')
            def close_spider(self, spider):
                print('我关了')
                self.f.close()
            # 这个很重要
            def process_item(self, item, spider):
                self.f.write(item['title']+'\n')
                return item
    -第三步:配置文件配置
    	ITEM_PIPELINES = {
           "firstscrapy.pipelines.FirstscrapyFilePipeline": 300,  # 数字越小,优先级越高
        }
        
    -第四步:在解析方法parse中yield item对象
    

4 全站爬取cnblogs文章

4.1 request和response对象传递参数

# Request创建:在parse中,for循环中,创建Request对象时,传入meta
	yield Request(url=url, callback=self.detail_parse,meta={'item':item})
    
# Response对象:detail_parse中,通过response取出meta取出item,把文章详情写入
	yield item

4.2 解析下一页并继续爬取

import scrapy
from bs4 import BeautifulSoup
from firstscrapy.items import FirstscrapyItem
from scrapy.http.request import Request

class CnblogsSpider(scrapy.Spider):
    name = "cnblogs"
    allowed_domains = ["www.cnblogs.com"]
    start_urls = ["http://www.cnblogs.com/"]

    # 解析出下一页地址,继续爬取
    def parse(self, response):
        article_list = response.xpath('//*[@id="post_list"]/article')
        # article_list = response.xpath('//article[contains(@class,"post-item")]')
        # l=[]
        for article in article_list:
            item = FirstscrapyItem()
            title = article.xpath('.//div/a/text()').extract_first()
            item['title'] = title
            author_img = article.xpath('.//div//img/@src').extract_first()
            item['author_img'] = author_img
            author_name = article.xpath('.//footer//span/text()').extract_first()
            item['author_name'] = author_name
            desc_old = article.xpath('.//p/text()').extract()
            desc = desc_old[0].replace('\n', '').replace(' ', '')
            if not desc:
                desc = desc_old[1].replace('\n', '').replace(' ', '')
            item['desc'] = desc
            url = article.xpath('.//div/a/@href').extract_first()
            item['url'] = url
            # print(title)
            # yield item对象 不完整,缺文章的content,而文章的content在下一个页面中,而此处要yield一个Request对象
            # yield item
            yield Request(url=url,callback=self.parser_detail,meta={'item':item}) # 爬完后执行的解析方法


        next='https://www.cnblogs.com'+response.css('div.pager>a:last-child::attr(href)').extract_first()
        print(next) # 继续爬取
        yield Request(url=next,callback=self.parse)



    # 解析详情的方法
    def parser_detail(self,response):
        # print(response.status)
        # 解析出文章内容
        content=response.css('#cnblogs_post_body').extract_first()
        # print(str(content))
        # 如何放到item中
        item=response.meta.get('item')
        if content:
            item['content']=content
        else:
            item['content'] = '没查到'
        yield item



posted @ 2023-03-15 17:04  雪语  阅读(39)  评论(0编辑  收藏  举报