爬虫基础

一、什么是爬虫?

   爬虫就是通过编写程序模拟浏览器上网,然后让其去互联网上抓取数据的过程。

二、爬虫的流程

  

三、预备知识

1、http协议

  HTTP请求由三部分组成,分别是:请求行,消息报头,请求正文

  HTTP响应也是由三个部分组成,分别是:状态行,消息报头,相应正文

 2.robots.txt协议

   如果自己的门户网站中的指定页面中的数据不想让爬虫程序爬取到的话,那么则可以通过编写一个robots.txt的协议文件来约束爬虫程序的数据爬取。robots协议的编写格式可以观察淘宝网的robots(访问www.taobao.com/robots.txt即可)。但是需要注意的是,该协议只是相当于口头的协议,并没有使用相关技术进行强制管制,所以该协议是防君子不防小人。但是我们在学习爬虫阶段编写的爬虫程序可以先忽略robots协议。

四、requests模块

 1.什么是requests模块?

  requests模块是python中原生的基于网络请求的模块,其主要作用是用来模拟浏览器发起请求。功能强大,用法简洁高效。在爬虫领域中占据着半壁江山的地位。

 2.安装

  pip install requests

 3.使用流程

  • 指定url
  • 基于requests模块发起请求
  • 获取响应对象中的数据值
  • 持久化存储

4. 基本语法

支持的请求:
import
requests requests.get("http://httpbin.org/get") requests.post("http://httpbin.org/post") requests.put("http://httpbin.org/put") requests.delete("http://httpbin.org/delete") requests.head("http://httpbin.org/get") requests.options("http://httpbin.org/get")

5.示例

import requests

#1.路由

url = 'https://www.sogou.com/' #2.调用get方法,该方法就可以根据指定的url发起请求,返回一个响应对象 response = requests.get(url=url,proxies={'https':'121.139.218.165:31409'}) #3.text属性就可以将响应对象中的数据值进行获取,text属性返回的数据类型是str page_text = response.text #print(page_text) #4. with open('./sogou.html','w',encoding='utf-8') as fp: fp.write(page_text) print('over')

response.content #返回的是二进制的页面数据
response.headers #返回的是响应头信息
response.encoding #返回的是响应对象中存储数据的原始编码格式
response.url#返回路由
response.status_code #返回状态码

通过5个基于requests模块的爬虫项目对该模块进行学习和巩固

基于requests模块的get请求

需求:爬取搜狗指定词条搜索后的页面数据

import requests

import os

#指定搜索关键字

word = input('enter a word you want to search:')

#自定义请求头信息

headers={

    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',

    }

#指定url

url = 'https://www.sogou.com/web'

#封装get请求参数

prams = {

    'query':word,

    'ie':'utf-8'

}

#发起请求

response = requests.get(url=url,params=param)



#获取响应数据

page_text = response.text



with open('./sougou.html','w',encoding='utf-8') as fp:

    fp.write(page_text)
View Code

基于requests模块的post请求

需求:登录豆瓣电影,爬取登录成功后的页面数据

import requests

import os

url = 'https://accounts.douban.com/login'

#封装请求参数

data = {

    "source": "movie",

    "redir": "https://movie.douban.com/",

    "form_email": "15027900535",

    "form_password": "bobo@15027900535",

    "login": "登录",

}

#自定义请求头信息

headers={

    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',

    }

response = requests.post(url=url,data=data)

page_text = response.text



with open('./douban111.html','w',encoding='utf-8') as fp:

    fp.write(page_text)
View Code

基于requests模块ajax的get请求

需求:爬取豆瓣电影分类排行榜 https://movie.douban.com/中的电影详情数据

#!/usr/bin/env python

# -*- coding:utf-8 -*-



import requests

import urllib.request

if __name__ == "__main__":



    #指定ajax-get请求的url(通过抓包进行获取)

    url = 'https://movie.douban.com/j/chart/top_list?'



    #定制请求头信息,相关的头信息必须封装在字典结构中

    headers = {

        #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数

        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',

    }



    #定制get请求携带的参数(从抓包工具中获取)

    param = {

        'type':'5',

        'interval_id':'100:90',

        'action':'',

        'start':'0',

        'limit':'20'

    }

    #发起get请求,获取响应对象

    response = requests.get(url=url,headers=headers,params=param)



    #获取响应内容:响应内容为json串

    print(response.text)
View Code

基于requests模块ajax的post请求

需求:爬取肯德基餐厅查询http://www.kfc.com.cn/kfccda/index.aspx中指定地点的餐厅数据

#!/usr/bin/env python

# -*- coding:utf-8 -*-



import requests

import urllib.request

if __name__ == "__main__":



    #指定ajax-post请求的url(通过抓包进行获取)

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



    #定制请求头信息,相关的头信息必须封装在字典结构中

    headers = {

        #定制请求头中的User-Agent参数,当然也可以定制请求头中其他的参数

        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',

    }



    #定制post请求携带的参数(从抓包工具中获取)

    data = {

        'cname':'',

        'pid':'',

        'keyword':'北京',

        'pageIndex': '1',

        'pageSize': '10'

    }

    #发起post请求,获取响应对象

    response = requests.get(url=url,headers=headers,data=data)



    #获取响应内容:响应内容为json串

    print(response.text)
View Code

综合练习

需求:爬取搜狗知乎指定词条指定页码下的页面数据

import requests

import os

#指定搜索关键字

word = input('enter a word you want to search:')

#指定起始页码

start_page = int(input('enter start page num:'))

end_page = int(input('enter end page num:'))

#自定义请求头信息

headers={

    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',

    }

#指定url

url = 'https://zhihu.sogou.com/zhihu'

#创建文件夹

if not os.path.exists('./sougou'):

    os.mkdir('./sougou')

for page in range(start_page,end_page+1):

    #封装get请求参数

    params = {

        'query':word,

        'ie':'utf-8',

        'page':str(page)

    }

    #发起post请求,获取响应对象

    response = requests.get(url=url,params=params)

    #获取页面数据

    page_text = response.text

    fileName = word+'_'+str(page)+'.html'

    filePath = './sougou/'+fileName

    with open(filePath,'w',encoding='utf-8') as fp:

        fp.write(page_text)

        print('爬取'+str(page)+'页结束')
View Code

抓包工具:Fiddler

https://www.cnblogs.com/miantest/p/7289694.html

Jupyter Notebook:https://www.cnblogs.com/nxld/p/6566380.html

6.cookie操作

cookie概念:当用户通过浏览器首次访问一个域名时,访问的web服务器会给客户端发送数据,以保持web服务器与客户端之间的状态保持,这些数据就是cookie。

cookie作用:我们在浏览器中,经常涉及到数据的交换,比如你登录邮箱,登录一个页面。我们经常会在此时设置30天内记住我,或者自动登录选项。那么它们是怎么记录信息的呢,答案就是今天的主角cookie了,Cookie是由HTTP服务器设置的,保存在浏览器中,但HTTP协议是一种无状态协议,在数据交换完毕后,服务器端和客户端的链接就会关闭,每次交换数据都需要建立新的链接。就像我们去超市买东西,没有积分卡的情况下,我们买完东西之后,超市没有我们的任何消费信息,但我们办了积分卡之后,超市就有了我们的消费信息。cookie就像是积分卡,可以保存积分,商品就是我们的信息,超市的系统就像服务器后台,http协议就是交易的过程。

思路:

  1.我们需要使用爬虫程序对人人网的登录时的请求进行一次抓取,获取请求中的cookie数据

  2.在使用个人信息页的url进行请求时,该请求需要携带 1 中的cookie,只有携带了cookie后,服务器才可识别这次请求的用户信息,方可响应回指定的用户信息页数据

import requests

if __name__ == "__main__":



    #登录请求的url(通过抓包工具获取)

    post_url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=201873958471'

    #创建一个session对象,该对象会自动将请求中的cookie进行存储和携带

    session = requests.session()

   #伪装UA

    headers={

        'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',

    }

    formdata = {

        'email': '17701256561',

        'icode': '',

        'origURL': 'http://www.renren.com/home',

        'domain': 'renren.com',

        'key_id': '1',

        'captcha_type': 'web_login',

        'password': '7b456e6c3eb6615b2e122a2942ef3845da1f91e3de075179079a3b84952508e4',

        'rkey': '44fd96c219c593f3c9612360c80310a3',

        'f': 'https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3Dm7m_NSUp5Ri_ZrK5eNIpn_dMs48UAcvT-N_kmysWgYW%26wd%3D%26eqid%3Dba95daf5000065ce000000035b120219',

    }

    #使用session发送请求,目的是为了将session保存该次请求中的cookie

    session.post(url=post_url,data=formdata,headers=headers)



    get_url = 'http://www.renren.com/960481378/profile'

    #再次使用session进行请求的发送,该次请求中已经携带了cookie

    response = session.get(url=get_url,headers=headers)

    #设置响应内容的编码格式

    response.encoding = 'utf-8'

    #将响应内容写入文件

    with open('./renren.html','w') as fp:

        fp.write(response.text)
View Code

7、基于requests模块的代理操作

一些网站会有相应的反爬虫措施,例如很多网站会检测某一段时间某个IP的访问次数,如果访问频率太快以至于看起来不像正常访客,它可能就会会禁止这个IP的访问。所以我们需要设置一些代理IP,每隔一段时间换一个代理IP,就算IP被禁止,依然可以换个IP继续爬取。

  • 免费代理ip提供网站

    • http://www.goubanjia.com/(推荐)

    • 西祠代理

    • 快代理

代理知识:

  透明代理:对方服务器知道你使用了代理,也知道你的真实IP

  匿名代理:对方服务器可以知道你使用了代理,但不知道你的真实IP

  高匿名代理:对方服务器不知道你使用了代理,更不知道你的真实IP

import requests

import random

if __name__ == "__main__":

    #不同浏览器的UA

    header_list = [

        # 遨游

        {"user-agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)"},

        # 火狐

        {"user-agent": "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"},

        # 谷歌

        {

            "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11"}

    ]

    #不同的代理IP

    proxy_list = [

        {"http": "112.115.57.20:3128"},

        {'http': '121.41.171.223:3128'}

    ]

    #随机获取UA和代理IP

    header = random.choice(header_list)

    proxy = random.choice(proxy_list)



    url = 'http://www.baidu.com/s?ie=UTF-8&wd=ip'

    #参数3:设置代理

    response = requests.get(url=url,headers=header,proxies=proxy)

    response.encoding = 'utf-8'

    

    with open('daili.html', 'wb') as fp:

        fp.write(response.content)

    #切换成原来的IP

    requests.get(url, proxies={"http": ""})
代码

8.验证码处理

使用云打码平台:http://www.yundama.com/

或者打码兔

打码平台使用流程

- 登录开发者用户
- 点击开发文档,进行示例代码的下载。下载的PythonHTTP示例
- 点击我的软件,创建一个软件
#代码执行前,一定要将当前类加载到内存中
import http.client, mimetypes, urllib, json, time, requests

class YDMHttp:

    apiurl = 'http://api.yundama.com/api.php'
    username = ''
    password = ''
    appid = ''
    appkey = ''

    def __init__(self, username, password, appid, appkey):
        self.username = username  
        self.password = password
        self.appid = str(appid)
        self.appkey = appkey

    def request(self, fields, files=[]):
        response = self.post_url(self.apiurl, fields, files)
        response = json.loads(response)
        return response
    
    def balance(self):
        data = {'method': 'balance', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
        response = self.request(data)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['balance']
        else:
            return -9001
    
    def login(self):
        data = {'method': 'login', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey}
        response = self.request(data)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['uid']
        else:
            return -9001

    def upload(self, filename, codetype, timeout):
        data = {'method': 'upload', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'codetype': str(codetype), 'timeout': str(timeout)}
        file = {'file': filename}
        response = self.request(data, file)
        if (response):
            if (response['ret'] and response['ret'] < 0):
                return response['ret']
            else:
                return response['cid']
        else:
            return -9001

    def result(self, cid):
        data = {'method': 'result', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid)}
        response = self.request(data)
        return response and response['text'] or ''

    def decode(self, filename, codetype, timeout):
        cid = self.upload(filename, codetype, timeout)
        if (cid > 0):
            for i in range(0, timeout):
                result = self.result(cid)
                if (result != ''):
                    return cid, result
                else:
                    time.sleep(1)
            return -3003, ''
        else:
            return cid, ''

    def report(self, cid):
        data = {'method': 'report', 'username': self.username, 'password': self.password, 'appid': self.appid, 'appkey': self.appkey, 'cid': str(cid), 'flag': '0'}
        response = self.request(data)
        if (response):
            return response['ret']
        else:
            return -9001

    def post_url(self, url, fields, files=[]):
        for key in files:
            files[key] = open(files[key], 'rb');
        res = requests.post(url, files=files, data=fields)
        return res.text
先运行
#需求:爬取基于某人个人用户的用户信息(人人网)

import requests
from lxml import etree
#该函数可以将验证码图片进行识别,返回内容
def getCode(imgPath):
        #示例编码
    # 普通用户名
    username    = 'bobo328410948'

    # 普通密码
    password    = 'bobo328410948'                            

    # 软件ID,开发者分成必要参数。登录开发者后台【我的软件】获得!
    appid       = 6003                                   

    # 软件密钥,开发者分成必要参数。登录开发者后台【我的软件】获得!
    appkey      = '1f4b564483ae5c907a1d34f8e2f2776c'    

    # 图片文件
    filename    = imgPath                        

    # 验证码类型,# 例:1004表示4位字母数字,不同类型收费不同。请准确填写,否则影响识别率。在此查询所有类型 http://www.yundama.com/price.html
    codetype    = 2004

    # 超时时间,秒
    timeout     = 20                                    

    # 检查
    if (username == 'username'):
        print('请设置好相关参数再测试')
    else:
        # 初始化
        yundama = YDMHttp(username, password, appid, appkey)

        # 登陆云打码
        uid = yundama.login();
        print('uid: %s' % uid)

        # 查询余额
        balance = yundama.balance();
        print('balance: %s' % balance)

        # 开始识别,图片路径,验证码类型ID,超时时间(秒),识别结果
        cid, result = yundama.decode(filename, codetype, timeout);
        print('cid: %s, result: %s' % (cid, result))
        return result

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

#获取验证码图片
url = 'http://www.renren.com/'
page_text = requests.get(url=url,headers=headers).text
tree = etree.HTML(page_text)
codeImg_url = tree.xpath('//*[@id="verifyPic_login"]/@src')[0]
#针对验证码图片的url发起请求,获取验证码图片的二进制数据,存储到本地
codeImg_data = requests.get(url=codeImg_url,headers=headers).content
with open('./code.jpg','wb') as fp:
    fp.write(codeImg_data)
#获取验证码图片中的内容
code_content = getCode('code.jpg')
print(code_content)

#创建一个session对象,该对象可以发起请求。该对象可以自动将请求后创建的cookie进行存储
session = requests.session()

#1.进行登录操作:服务器就会针对指定当前用户创建一个cookie对象(存储当前用户的状态信息(包含当前用户的身份标识))
#2.进行个人主页请求(携带步骤1中创建的cookie),获取当前用户个人主页的页面数据

#进行登录操作
url = 'http://www.renren.com/ajaxLogin/login?1=1&uniqueTimestamp=2018921047557'

data = {
    "email":"www.zhangbowudi@qq.com",
    "icode":code_content,
    "origURL":"http://www.renren.com/home",
    "domain":"renren.com",
    "key_id":"1",
    "captcha_type":"web_login",
    "password":"5bc148793949a14590c2a43e32204fe4e486b821cd4641abaecba424fa27336d",
    "rkey":"142a1b216077e29550fc37b2057fa297",
    "f":"http%3A%2F%2Fwww.renren.com%2F289676607%2Fprofile",
}
#目的:创建cookie
session.post(url=url,data=data,headers=headers)  #cookie会存储到session

#针对个人主页发起请求
person_url = 'http://www.renren.com/289676607/profile'
page_text = session.get(url=person_url,headers=headers).text

with open('./renren.html','w',encoding='utf-8') as fp:
    fp.write(page_text)

五、三种数据解析方式

一.正解解析

 单字符:

  . : 除换行以外所有字符

  [] :[aoe] [a-w] 匹配集合中任意一个字符

  d :数字 [0-9]

  D : 非数字

  w :数字、字母、下划线、中文

  W : 非w

  s :所有的空白字符包,括空格、制表符、换页符等等。等价于 [ f v]。

  S : 非空白

数量修饰:

  * : 任意多次 >=0

  + : 至少1次 >=1

  ? : 可有可无 0次或者1次

  {m} :固定m次 hello{3,}

  {m,} :至少m次

  {m,n} :m-n次

边界:

  $ : 以某某结尾

  ^ : 以某某开头

分组:

  (ab)

贪婪模式: .*

非贪婪(惰性)模式: .*?

re.I : 忽略大小写

re.M :多行匹配

re.S :单行匹配

re.sub(正则表达式, 替换内容, 字符串)

import re

#提取出python

key="javapythonc++php"

re.findall('python',key)[0]

#####################################################################

#提取出hello world

key="<html><h1>hello world<h1></html>"

re.findall('<h1>(.*)<h1>',key)[0]

#####################################################################

#提取170

string = '我喜欢身高为170的女孩'

re.findall('d+',string)

#####################################################################

#提取出http://和https://

key='http://www.baidu.com and https://boob.com'

re.findall('https?://',key)

#####################################################################

#提取出hello

key='lalala<hTml>hello</HtMl>hahah' #输出<hTml>hello</HtMl>

re.findall('<[Hh][Tt][mM][lL]>(.*)</[Hh][Tt][mM][lL]>',key)

#####################################################################

#提取出hit. 

key='bobo@hit.edu.com'#想要匹配到hit.

re.findall('h.*?.',key)

#####################################################################

#匹配sas和saas

key='saas and sas and saaas'

re.findall('sa{1,2}s',key)

#####################################################################

#匹配出i开头的行

string = '''fall in love with you

i love you very much

i love she

i love her'''



re.findall('^.*',string,re.M)

#####################################################################

#匹配全部行

string1 = """<div>静夜思

窗前明月光

疑是地上霜

举头望明月

低头思故乡

</div>"""



re.findall('.*',string1,re.S)
正则示例
项目需求:爬取糗事百科指定页面的糗图,并将其保存到指定文件夹中 
import requests

import re
import os

if __name__ == "__main__":
     url = 'https://www.qiushibaike.com/pic/%s/'
     headers={
         'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
     }
     #指定起始也结束页码
     page_start = int(input('enter start page:'))
     page_end = int(input('enter end page:'))

     #创建文件夹
     if not os.path.exists('images'):
         os.mkdir('images')

     #循环解析且下载指定页码中的图片数据
     for page in range(page_start,page_end+1):
         print('正在下载第%d页图片'%page)
         new_url = format(url % page)
         response = requests.get(url=new_url,headers=headers)

         #解析response中的图片链接
         e = '<div class="thumb">.*?<img src="(.*?)".*?>.*?</div>'
         pa = re.compile(e,re.S)
         image_urls = pa.findall(response.text)

          #循环下载该页码下所有的图片数据
         for image_url in image_urls:
             image_url = 'https:' + image_url
             image_name = image_url.split('/')[-1]
             image_path = 'images/'+image_name

             image_data = requests.get(url=image_url,headers=headers).content
             with open(image_path,'wb') as fp:
                 fp.write(image_data)

二.Xpath解析

属性定位:

    #找到class属性值为song的div标签
    //div[@class="song"] 

层级&索引定位:

    #找到class属性值为tang的div的直系子标签ul下的第二个子标签li下的直系子标签a
    //div[@class="tang"]/ul/li[2]/a

逻辑运算:

    #找到href属性值为空且class属性值为du的a标签
    //a[@href="" and @class="du"]

模糊匹配:
    //div[contains(@class, "ng")]
    //div[starts-with(@class, "ta")]

取文本:

    # /表示获取某个标签下的文本内容
    //div[@class="song"]/p[1]/text()
  # //表示获取某个标签下的文本内容和所有子标签下的文本内容
  //div[@class="tang"]//text() 取属性: //div[@class="tang"]//li[2]/a/@href

代码中使用xpath表达式进行数据解析:

1.下载:pip install lxml
2.导包:from lxml import etree
3.将html文档或者xml文档转换成一个etree对象,然后调用对象中的方法查找指定的节点
  2.1 本地文件:tree = etree.parse(文件名)
                tree.xpath("xpath表达式")
  2.2 网络数据:tree = etree.HTML(网页内容字符串)
                tree.xpath("xpath表达式")
项目需求:获取好段子中段子的内容和作者   http://www.haoduanzi.com

from lxml import etree
import requests

url='http://www.haoduanzi.com/category-10_2.html'
headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36',
    }
url_content=requests.get(url,headers=headers).text

#使用xpath对url_conten进行解析
#使用xpath解析从网络上获取的数据
tree=etree.HTML(url_content)

#解析获取当页所有段子的标题
title_list=tree.xpath('//div[@class="log cate10 auth1"]/h3/a/text()')

ele_div_list=tree.xpath('//div[@class="log cate10 auth1"]')

text_list=[] #最终会存储12个段子的文本内容
for ele in ele_div_list:

    #段子的文本内容(是存放在list列表中)
    text_list=ele.xpath('./div[@class="cont"]//text()')

    #list列表中的文本内容全部提取到一个字符串中
    text_str=str(text_list)

    #字符串形式的文本内容防止到all_text列表中
    text_list.append(text_str)
print(title_list)
print(text_list)

三.BeautifulSoup解析

基础使用

使用流程:       

    - 导包:from bs4 import BeautifulSoup
    - 使用方式:可以将一个html文档,转化为BeautifulSoup对象,然后通过对象的方法或者属性去查找指定的节点内容

        (1)转化本地文件:
             - soup = BeautifulSoup(open('本地文件'), 'lxml')
        (2)转化网络文件:
             - soup = BeautifulSoup('字符串类型或者字节类型', 'lxml')
        (3)打印soup对象显示内容为html文件中的内容

基础巩固:

    (1)根据标签名查找
        - soup.a   只能找到第一个符合要求的标签

    (2)获取属性
        - soup.a.attrs  获取a所有的属性和属性值,返回一个字典
        - soup.a.attrs['href']   获取href属性
        - soup.a['href']   也可简写为这种形式

    (3)获取内容
        - soup.a.string
        - soup.a.text
        - soup.a.get_text()

       【注意】如果标签还有标签,那么string获取到的结果为None,而其它两个,可以获取文本内容

    (4)find:找到第一个符合要求的标签
        - soup.find('a')  找到第一个符合要求的
        - soup.find('a', title="xxx")
        - soup.find('a', alt="xxx")
        - soup.find('a', class_="xxx")
        - soup.find('a', id="xxx")

    (5)find_all:找到所有符合要求的标签
        - soup.find_all('a')
        - soup.find_all(['a','b']) 找到所有的a和b标签
        - soup.find_all('a', limit=2)  限制前两个

    (6)根据选择器选择指定的内容
               select:soup.select('#feng')
        - 常见的选择器:标签选择器(a)、类选择器(.)、id选择器(#)、层级选择器
            - 层级选择器:
                div .dudu #lala .meme .xixi  下面好多级
                div > p > a > .lala          只能是下面一级
        【注意】select选择器返回永远是列表,需要通过下标提取指定的对象
需求:使用bs4实现将诗词名句网站中三国演义小说的每一章的内容爬去到本地磁盘进行存储   http://www.shicimingju.com/book/sanguoyanyi.html 
import requests
from bs4 import BeautifulSoup

headers={
         'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
     }
def parse_content(url):

    #获取标题正文页数据
    page_text = requests.get(url,headers=headers).text
    soup = BeautifulSoup(page_text,'lxml')

    #解析获得标签
    ele = soup.find('div',class_='chapter_content')
    content = ele.text #获取标签中的数据值
    return content

if __name__ == "__main__":
     url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
     reponse = requests.get(url=url,headers=headers)
     page_text = reponse.text

     #创建soup对象
     soup = BeautifulSoup(page_text,'lxml')

     #解析数据
     a_eles = soup.select('.book-mulu > ul > li > a')
     print(a_eles)
     cap = 1
     for ele in a_eles:

         print('开始下载第%d章节'%cap)
         cap+=1
         title = ele.string
         content_url = 'http://www.shicimingju.com'+ele['href']
         content = parse_content(content_url)

         with open('./sanguo.txt','w') as fp:
             fp.write(title+":"+content+'




')
             print('结束下载第%d章节'%cap)

 

六、selenuim和phantonJs处理网页动态加载数据的爬取

一.图片懒加载

  • 图片懒加载概念:

    • 图片懒加载是一种网页优化技术。图片作为一种网络资源,在被请求时也与普通静态资源一样,将占用网络资源,而一次性将整个页面的所有图片加载完,将大大增加页面的首屏加载时间。为了解决这种问题,通过前后端配合,使图片仅在浏览器当前视窗内出现时才加载该图片,达到减少首屏图片请求数的技术就被称为“图片懒加载”。

  • 网站一般如何实现图片懒加载技术呢?

    • 在网页源码中,在img标签中首先会使用一个“伪属性”(通常使用src2,original......)去存放真正的图片链接而并非是直接存放在src属性中。当图片出现到页面的可视化区域中,会动态将伪属性替换成src属性,完成图片的加载。

二.selenium

  • 什么是selenium?
    • 是Python的一个第三方库,对外提供的接口可以操作浏览器,然后让浏览器完成自动化的操作。  

  • 环境搭建

    • 安装selenum:pip install selenium

    • 获取某一款浏览器的驱动程序(以谷歌浏览器为例)

      • 谷歌浏览器驱动下载地址:http://chromedriver.storage.googleapis.com/index.html

      • 下载的驱动程序必须和浏览器的版本统一,大家可以根据http://blog.csdn.net/huilan_same/article/details/51896672中提供的版本映射表进行对应

from selenium import webdriver
from time import sleep


# 后面是你的浏览器驱动位置,记得前面加r'','r'是防止字符转义的
driver = webdriver.Chrome(r'驱动程序路径')

# 用get打开百度页面
driver.get("http://www.baidu.com")

# 查找页面的“设置”选项,并进行点击
driver.find_elements_by_link_text('设置')[0].click()

sleep(2)

# # 打开设置后找到“搜索设置”选项,设置为每页显示50条
driver.find_elements_by_link_text('搜索设置')[0].click()

sleep(2)


# 选中每页显示50条
m = driver.find_element_by_id('nr')
sleep(2)

m.find_element_by_xpath('//*[@id="nr"]/option[3]').click()
m.find_element_by_xpath('.//option[3]').click()
sleep(2)


# 点击保存设置
driver.find_elements_by_class_name("prefpanelgo")[0].click()
sleep(2)


# 处理弹出的警告页面   确定accept() 和 取消dismiss()
driver.switch_to_alert().accept()
sleep(2)

# 找到百度的输入框,并输入 美女
driver.find_element_by_id('kw').send_keys('美女')
sleep(2)

# 点击搜索按钮
driver.find_element_by_id('su').click()
sleep(2)

# 在打开的页面中找到“Selenium - 开源中国社区”,并打开这个页面
driver.find_elements_by_link_text('美女_百度图片')[0].click()

sleep(3)


# 关闭浏览器
driver.quit()

代码介绍:

#导包
from selenium import webdriver  

#创建浏览器对象,通过该对象可以操作浏览器
browser = webdriver.Chrome('驱动路径')

#使用浏览器发起指定请求
browser.get(url)


#使用下面的方法,查找指定的元素进行操作即可

    find_element_by_id            根据id找节点
    find_elements_by_name         根据name找
    find_elements_by_xpath        根据xpath查找
    find_elements_by_tag_name     根据标签名找
    find_elements_by_class_name   根据class名字查找

三.phantomJs

  • PhantomJS是一款无界面的浏览器,其自动化操作流程和上述操作谷歌浏览器是一致的。由于是无界面的,为了能够展示自动化操作流程,PhantomJS为用户提供了一个截屏的功能,使用save_screenshot函数实现。
from selenium import webdriver
import time

# phantomjs路径
path = r'PhantomJS驱动路径'

browser = webdriver.PhantomJS(path)

# 打开百度
url = 'http://www.baidu.com/'
browser.get(url)

time.sleep(3)

browser.save_screenshot(r'phantomjsaidu.png')

# 查找input输入框
my_input = browser.find_element_by_id('kw')

# 往框里面写文字
my_input.send_keys('美女')
time.sleep(3)

#截屏
browser.save_screenshot(r'phantomjsmeinv.png')

# 查找搜索按钮
button = browser.find_elements_by_class_name('s_btn')[0]
button.click()

time.sleep(3)
browser.save_screenshot(r'phantomjsshow.png')

time.sleep(3)
browser.quit()

重点:selenium+phantomjs 就是爬虫终极解决方案:有些网站上的内容信息是通过动态加载js形成的,所以使用普通爬虫程序无法回去动态加载的js内容。例如豆瓣电影中的电影信息是通过下拉操作动态加载更多的电影信息。

from selenium import webdriver
from time import sleep
import time

if __name__ == '__main__':
    url = 'https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action='

    # 发起请求前,可以让url表示的页面动态加载出更多的数据
    path = r'C:UsersAdministratorDesktop爬虫授课day05ziliaophantomjs-2.1.1-windowsinphantomjs.exe'

    # 创建无界面的浏览器对象
    bro = webdriver.PhantomJS(path)

    # 发起url请求
    bro.get(url)
    time.sleep(3)

    # 截图
    bro.save_screenshot('1.png')


    # 执行js代码(让滚动条向下偏移n个像素(作用:动态加载了更多的电影信息))
    js = 'window.scrollTo(0,document.body.scrollHeight)'
    bro.execute_script(js)  # 该函数可以执行一组字符串形式的js代码
    time.sleep(2)

    bro.execute_script(js)  # 该函数可以执行一组字符串形式的js代码
    time.sleep(2)
    bro.save_screenshot('2.png') 
    time.sleep(2) 

    # 使用爬虫程序爬去当前url中的内容 
    html_source = bro.page_source # 该属性可以获取当前浏览器的当前页的源码(html) 
    with open('./source.html', 'w', encoding='utf-8') as fp: 
        fp.write(html_source) 

    bro.quit()

四.谷歌无头浏览器

  • 由于PhantomJs最近已经停止了更新和维护,所以推荐大家可以使用谷歌的无头浏览器,是一款无界面的谷歌浏览器。
  • 代码展示:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time 

# 创建一个参数对象,用来控制chrome以无界面模式打开
chrome_options = Options()
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')

# 驱动路径
path = r'C:UsersBLiDesktop1801day05ziliaochromedriver.exe'

# 创建浏览器对象
browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)

# 上网
url = 'http://www.baidu.com/'
browser.get(url)
time.sleep(3)

browser.save_screenshot('baidu.png')
browser.quit()
原文地址:https://www.cnblogs.com/Mr-Murray/p/9892926.html