python(六)动态网页爬虫

什么是动态网页爬虫

动态网页

在网站不重新加载的情况下(网页的url不发生改变),通过ajax技术动态更新网站中的局部数据。

ajax

异步JavaScript和XML,前端与服务器进行少量数据交换,ajax可以使网页实现异步更新。这意味着在不重新加载整个网页的情况下,对网页的某部分进行更新。传统的网页,如果需要更新内容,必须重载整个页面。因为传输数据的时候使用的是XML语法。而ajax在数据交互的时候基本上使用JSON,使用ajax加载的数据,即使使用了JS,将数据渲染到浏览器中,在查看源代码的时候还是看不到ajax加载的数据,只能看到使用这个url加载的html代码

动态网页爬虫的解决方案

  1. 直接分析Ajax调用的接口,然后通过代码请求这个接口
  2. 使用selenium+Chromedriver模拟浏览器行为获取数据
方式 优点 缺点
分析接口 直接可以请求到数据,不需要做一些解析工作。代码量少,性能高 分析接口比较复杂,特别是一些通过JS混淆的接口,需要一定的JS功底,容易被发现
selenium 直接模拟浏览器的行为。浏览器能请求到的,使用selenium也能请求到,爬虫更稳定 代码量多,性能低

selenium

  1. 是一个web自动化测试工具,可以直接运行在浏览器上
  2. 支持所有主流的浏览器
  3. 可以根据我们的指令,让浏览器自动加载页面,获取需要的数据,基础页面截图等

自动截图并获取网页源代码

from selenium import webdriver
# 构造浏览器
chorme = webdriver.Chrome()
# 请求的url
chorme.get('http://www.baidu.com')
# 截图操作
chorme.save_screenshot('baidu.jpg')
# 获取网页源代码
html = chorme.page_source
print(html)
chorme.quit()

selenium的基本使用

定位元素
  1. find_element获取满足条件的第一个元素
  2. find_elements获取满足条件的所有元素
序号 方法 描述
1 find_element_by_id() 通过ID定位元素
2 find_element_by_name() 通过name定位元素
3 find_element_by_class_name() 通过类样式名称定位元素
4 find_element_by_tag_name() 通过标签名称定位元素
5 find_element_by_link_text() 通过链接定位元素(a标签)
6 find_element_by_css_selector() 通过CSS定位元素
7 find_element_by_xpath() 通过xpath语法来获取元素
from selenium import webdriver
import time
# 构造浏览器
chorme = webdriver.Chrome()
# 请求的url
chorme.get('http://cn.bing.com')
# 定位元素
# (1)通过id定位元素
# input_tag = chorme.find_element_by_id('sb_form_q')
# (2) 通过name属性定位元素
# input_tag = chorme.find_element_by_name('q')
# (3) 通过类样式的名称定位元素
# input_tag = chorme.find_element_by_class_name('b_searchbox')
# (4) 通过标签名称定位元素
# input_tag = chorme.find_element_by_tag_name('input')
# input_tags = chorme.find_elements_by_tag_name('input') #获取满足条件的所有元素
# print(input_tags)
# input_tag.send_keys('python')
# (5) 根据链接文本定位元素
# a_tag = chorme.find_element_by_link_text('地图')
# a_tag.click()
# (6) 通过CSS样式定位元素
# input_tag = chorme.find_element_by_css_selector('#sb_form_q')
# input_tag = chorme.find_element_by_css_selector('.b_searchbox')
# (7) 通过xpath定位元素
input_tag = chorme.find_element_by_xpath('//input[@class="b_searchbox"]')
input_tag.send_keys('python')
操作表单元素

操作输入框:通过selenium的定位方式找到元素,使用send_keys(value)将值填入
操作复选框和按钮:因为要选中的checkbox(复选框)标签,在网页中是通过鼠标点击的。因此想要选中checkbox标签,然后执行click()事件
下拉列表框select:select元素不能直接点击,因为点击后还需要选中元素
selenium.webdriver.support.ui.Select类专门操作select元素,将获取到的元素当成参数传到这个类中,创建这个类的对象,就可以使用该对象进行选择

from selenium import webdriver
import time
from selenium.webdriver.support.ui import Select
# 构造浏览器
chorme = webdriver.Chrome()
# 请求的url
# chorme.get('http://www.baidu.com')
# 1.操作表单元素-》输入框
# input_tag = chorme.find_element_by_id('kw')
# input_tag.send_keys('你好')

# 2.操作表单元素-》复选框
# chorme.get('https://account.cnblogs.com/signin')
# checkbox_tag = chorme.find_element_by_id('mat-checkbox-1')
# checkbox_tag.click()
# 3. 操作表单元素-》下拉列表
chorme.get('https://kyfw.12306.cn/otn/regist/init')
# 创建select对象
select = Select(chorme.find_element_by_id('cardType'))
# 获取下拉列表中的项
# (1)根据索引获取
# select.select_by_index(1)
# (2) 根据value属性
# select.select_by_value('B')
# (3) 根据可见文本
select.select_by_visible_text('台湾居民来往大陆通行证')
time.sleep(5)
chorme.quit()

selenium的行为链

为什么需要行为链?

有些网站可能会在浏览器端做一些验证行为是否符合人类的行为做反爬
有更多的复杂操作,在自动化测试中经常使用

行为链如何使用
  1. 导入from selenium.webdriver.common.action_chains import ActionChains
  2. 创建对象 actions=ActionChains(driver)
  3. 移动到元素actions.move_to_element(element)
  4. 文本框填入内容 actions.send_keys_to_element(element,‘python’)
  5. 单击 actions.click(element)
from selenium import webdriver
import time
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains # selenium的行为链
# 构造浏览器
driver = webdriver.Chrome()
# 请求的url
driver.get('https://cn.bing.com')
input_tag = driver.find_element_by_id('sb_form_q') # 获取文本框
button = driver.find_element_by_id('sb_form_go') # 获取提交按钮
# 创建行为链(更好的模拟人类的行为)
actions = ActionChains(driver)
actions.move_to_element(input_tag) # 移动到文本框上
actions.send_keys_to_element(input_tag,'python') # 输入要搜索的关键字 python
actions.move_to_element(button) # 移动到提交按钮
actions.click(button) # 点击提交按钮
# 开始执行行为链
actions.perform()
'''
    更多的鼠标操作行为
    双击-》double_click(element)
    右键点击-》context_click(element)
'''

操作cookie

from selenium import webdriver
import time
from selenium.webdriver.support.ui import Select
from selenium.webdriver.common.action_chains import ActionChains # selenium的行为链
# 构造浏览器
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
# (1) 获取所有的cookie信息
cookies = driver.get_cookies()
for cookie in cookies:
    print(cookie)

print('----------------------')
# (2) 获取指定的cookie信息
cookie = driver.get_cookie('BAIDUID')
print(cookie)
print('----------------------')
# (3) 添加cookie信息
driver.add_cookie({'name':'zhangsan','value':'8888'})
for item in driver.get_cookies():
    print(item)
print('----------------------')
print(driver.get_cookie('zhangsan'))
print('----------------------')
# (4)删除cookie
driver.delete_cookie('zhangsan')
print(driver.get_cookie('zhangsan'))
print('----------------------')
# (5) 删除所有的cookie信息
driver.delete_all_cookies() #删除浏览器端的所有cookie信息
print(driver.get_cookies())

页面等待

隐式等待:调用dirver.implicitly_wait,在获取不可用的元素之前,会先等待N秒钟的时间
显示等待:显示等待是表明某个条件成立后才执行获取元素的操作。也可以在等待的时候指定一个最大时间,如果超过这个时间那么就会抛出一个异常。

driver = webdriver.Chrome()
'''driver.get('http://www.baidu.com')
# 隐式等待
driver.implicitly_wait(5)
driver.find_element_by_id('abc')'''

# 显示等待
driver.get('https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc')
WebDriverWait(driver,100).until(
    ec.text_to_be_present_in_element_value((By.ID,'fromStationText'),'上海')
)
WebDriverWait(driver,100).until(
    ec.text_to_be_present_in_element_value((By.ID,'toStationText'),'北京')
)
button = driver.find_element_by_id('query_ticket')
button.click()

半自动订票

import re

import requests
from selenium import webdriver
from selenium.common.exceptions import ElementNotInteractableException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

import openpyxl
# 定义一个谷歌驱动
driver = webdriver.Chrome()
class GetStationInfo:

    @staticmethod
    def get_station_info():
        url = 'https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9163'
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'}
        resp = requests.get(url,headers=headers)
        # print(resp.text)
        # 通过正则表达式,筛选车站名称和代码(返回一个列表)
        stations = re.findall('([\u4e00-\u9fa5]+)\|([A-Z]+)',resp.text)
        return stations
    # 将车站代码保存到xlsx中
    def save_stations(self,stations):
        wb = openpyxl.Workbook() # 创建工作簿
        ws = wb.active # sheet设置活动属性
        for item in stations:
            # print(item)
            ws.append(item) # 向sheet中添加数据,一次添加一行
        wb.save('所有车站名称及代码.xlsx')

class HalfAutoOrderTicket:
    '''
        O :9: 二等座
        M :8: 一等座
        9 :7: 商务座
        6:10:高级软卧
        4:11:软卧一等卧
        12:动卧
        3:13:硬卧 二等卧
        14:软座
        1:15:硬座
        16:无座
        17:其他
    '''
    value_tindex = {'O': 9, 'M': 8,'9':7,'6':10,'4':11,'3':13,'1':15} # 将座席与train的索引对应
    seats = {'O': '二等座', 'M': '一等座','1':'硬座','3':'硬卧','4':'软卧一等卧','9':'商务座','6':'高级软卧'} # 将座席与train的索引对应
    login_url = 'https://kyfw.12306.cn/otn/resources/login.html' # 登陆界面
    per_center_url = 'https://kyfw.12306.cn/otn/view/index.html' # 个人中心界面
    search_url = 'https://kyfw.12306.cn/otn/leftTicket/init?linktypeid=dc' # 查询车次界面
    confirm_url = 'https://kyfw.12306.cn/otn/confirmPassenger/initDc' # 确认乘车人界面

    # 定义出发地,目的地,出发日,{车次,席别},座位
    def __init__(self,from_station,to_station,train_date,d_trains,l_name):
        self.from_station = from_station
        self.to_station = to_station
        self.train_date = train_date
        self.d_trains = d_trains
        self.l_name = l_name
        self.stations =self.get_stations()
        self.train_no = None
        self.seat = None
        self.seat_types_org = None

    def login(self):
        # 进入登录界面
        driver.get(self.login_url)
        # 等待输入密码,或扫码登陆,直到跳转到个人中心界面
        WebDriverWait(driver,1000).until(
            ec.url_to_be(self.per_center_url)
        )

    def search_order_ticket(self):
        driver.get(self.search_url)
        # 获取出发地,目的地,出发日输入框
        from_station_input = driver.find_element_by_id('fromStation')
        to_station_input = driver.find_element_by_id('toStation')
        train_date_input = driver.find_element_by_id('train_date')

        # 根据值获取到车站的代码
        from_station_code = self.stations[self.from_station]
        to_station_code = self.stations[self.to_station]
        # 将值填充到相应的输入框中
        driver.execute_script(f'arguments[0].value="{from_station_code}"',from_station_input)
        driver.execute_script(f'arguments[0].value="{to_station_code}"',to_station_input)
        driver.execute_script(f'arguments[0].value="{self.train_date}"',train_date_input)

        # 点击查询
        search_tag = driver.find_element_by_xpath('//a[@id="query_ticket"]')
        search_tag.click()

        # 等待加载车次信息框
        WebDriverWait(driver,1000).until(
            ec.presence_of_element_located((By.XPATH,'//tbody[@id="queryLeftTable"]/tr'))
        )

        # 筛选有用的数据
        trains_info = driver.find_elements_by_xpath('//tbody[@id="queryLeftTable"]/tr[not(@datatran)]')
        have_ticket = False
        while True:
            for train in trains_info:
                train_lst = train.text.replace('\n',' ').split(' ')
                train_no = train_lst[0] # 表示车次
                if train_no in self.d_trains: # 要购买的车次
                    # 设置要购买的座席
                    seat_types = self.d_trains[train_no]
                    if len(seat_types) > 0:
                        self.seat_types_org = seat_types
                    else:
                        self.seat_types_org = self.seats.keys()
                    for or_seat in seat_types:
                        seat = train_lst[self.value_tindex[or_seat]]  # 获取想预订的座席
                        if seat.isdigit() or seat == '有':
                            self.train_no = train_no
                            self.seat = or_seat
                            have_ticket = True
                            break
                    if have_ticket:
                        # 点击预订
                        order_tag = train.find_element_by_xpath('.//a[@class="btn72"]')
                        order_tag.click()
                        return
            else: # 想要买的车次都没有,就按照座席顺序查找有票的车次
                for train in trains_info:
                    train_lst = train.text.replace('\n', ' ').split(' ')
                    train_no = train_lst[0]  # 表示车次
                    for value in self.d_trains.values():
                        self.seat_types_org = value
                    if len(self.seat_types_org) == 0:
                        self.seat_types_org = self.seats.keys()
                        # self.seat_types_org = self.d_trains.values()[0]
                        # print(seat_types)
                    for or_seat in self.seat_types_org:
                        seat = train_lst[self.value_tindex[or_seat]]
                        if seat.isdigit() or seat == '有':
                            self.train_no = train_no
                            self.seat = or_seat
                            have_ticket = True
                            break
                    if have_ticket:
                        # 点击预订
                        order_tag = train.find_element_by_xpath('.//a[@class="btn72"]')
                        order_tag.click()
                        return
            if not have_ticket:
                for train in trains_info:
                    train_lst = train.text.replace('\n', ' ').split(' ')
                    train_no = train_lst[0]  # 表示车次
                    self.seat_types_org = self.seats.keys()
                    # print(seat_types)
                    for or_seat in self.seat_types_org:
                        seat = train_lst[self.value_tindex[or_seat]]
                        if seat.isdigit() or seat == '有':
                            self.train_no = train_no
                            self.seat = or_seat
                            have_ticket = True
                            break
                    if have_ticket:
                        # 点击预订
                        order_tag = train.find_element_by_xpath('.//a[@class="btn72"]')
                        order_tag.click()
                        return
                print('票已经全部卖完,暂时没票')

    def confirm(self):
        # 等待进入确认乘客信息界面
        WebDriverWait(driver,1000).until(
            ec.url_to_be(self.confirm_url)
        )
        # 等待加载乘客信息界面
        WebDriverWait(driver,1000).until(
            ec.presence_of_element_located((By.XPATH,'//ul[@id="normal_passenger_id"]/li/label'))
        )
        # 获取乘车人信息
        passengers = driver.find_elements_by_xpath('//ul[@id="normal_passenger_id"]/li/label')
        for passenger in passengers:
            # print(passenge.text)
            if passenger.text in self.l_name:
                # 乘车人在传的列表中就,就选中这个人
                passenger.click()
        # 获取席位信息
        seat_select = Select(driver.find_element_by_id('seatType_1'))
        seat_types = self.seat_types_org
        for seat in seat_types:
            try:
                seat_select.select_by_value(seat)
                self.seat = seat
            except:
                continue
            else:
                break

        # 点击提交订单
        submit_tag = driver.find_element_by_id('submitOrder_id')
        submit_tag.click()
        # 等待加载核对信息对话框
        WebDriverWait(driver,1000).until(
            ec.presence_of_element_located((By.CLASS_NAME,'dhtmlx_window_active'))
        )

        # 等待确认按钮可以点击
        WebDriverWait(driver,1000).until(
            ec.element_to_be_clickable((By.ID,'qr_submit_id'))
        )

        qr_submit = driver.find_element_by_id('qr_submit_id')
        while qr_submit:
            try:
                qr_submit.click()
                qr_submit = driver.find_element_by_id('qr_submit_id')
            except ElementNotInteractableException:
                break
        print(f'恭喜抢票成功,抢到的票为:{self.train_no}车次,{self.seats[self.seat]}')


    # 获取车站代码
    def get_stations(self):
        while True:
            try:
                wb = openpyxl.load_workbook('所有车站名称及代码.xlsx')
                break
            except FileNotFoundError:
                g = GetStationInfo()
                stations = g.get_station_info()
                g.save_stations(stations)

        ws = wb.active # 设置活动
        lst = [] # 存储车站代码和名称
        for row in ws.rows:
            sub_list = []
            for cell in row:
                sub_list.append(cell.value)
            lst.append(sub_list)
        return dict(lst)


    # 启动程序
    def start(self):
        # 1.登陆
        self.login()
        # 2.查询,并预订车票
        self.search_order_ticket()
        # 3.选择座席,乘车人
        self.confirm()

class InputInfo:
    seats = {'O': '二等座', 'M': '一等座', '9': '商务座', '6': '高级软卧', '4': '软卧一等卧', '3': '硬卧', '1': '硬座'}
    def input_info(self):
        from_station = input('出发地:')
        to_station = input('目的地:')
        station_date = input('出发日期(如:2020-11-29):')
        train_no = input('车次(可不填):')
        seat_types = input('席别(如:二等座,一等座):')
        names = input('乘车人(如:张三,李四):')
        l_names = names.replace(',',',').split(',')
        seat_types = seat_types.replace(',',',').split(',')
        new_seat_types = []
        for seat in seat_types:
            for key,value in self.seats.items():
                if seat == value:
                    new_seat_types.append(key)
        d_train = dict(train_no=new_seat_types)
        d = {}
        d['from_station'] = from_station
        d['to_station'] = to_station
        d['station_date'] = station_date
        d['d_train'] = d_train
        d['l_names'] = l_names
        return d

if __name__ == '__main__':
    i = InputInfo()
    d = i.input_info()
    t = HalfAutoOrderTicket(d['from_station'],d['to_station'],d['station_date'],d['d_train'],d['l_names'])
    t.start()

字体反爬

原理:网页开发者自己创建一种字体,因为在字体中每个文字都有其代码,那么以后在网页中不会直接显示这个文字的最终的效果,而是显示他的代号,因此即使取到了网页中的文本内容,也只是获取到文字的代码,而不是文字本身

寻找字体

一般情况下为了考虑网页的渲染功能,通常网站的开发者会把字体编码成base64的方式,因此我们可以到网页中找到@font_face属性,然后获取里面的base64代码,再用python代码进行解码,然后再保存本地
如果没有使用base64,还有另外一种方式,就是直接把字体放到服务器上,然后前端通过@font_face中的url函数进行加载

分析字体

分析字体需要将字转换成xml文件,然后查看其中的cmap和glyf中的属性。其中cmap存储的是code和name的映射,而glyf中存储的是每个name下的字体绘制规则

import base64
import io
from fontTools.ttLib import TTFont
font_face = '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'
online_face = TTFont(io.BytesIO(base64.b64decode(font_face)))
# 保存成xml文件
# online_face.saveXML('58.xml')
online_face.save('58.ttf')

实习僧字体反爬

import requests
from bs4 import BeautifulSoup
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36'}
def send_request():
    url = 'https://www.shixiseng.com/interns?keyword=python'
    resp = requests.get(url,headers=headers)
    # print(resp.text)
    return resp.text

def detail_url(url):
    html = requests.get(url,headers=headers)
    bs = BeautifulSoup(html.text,'html.parser')
    title = bs.title.text # 获取职位名称
    # 获取公司名称
    com_name = bs.select('.com_intro .com-name')[0].text
    # 获取薪水
    salary = bs.select('.job_money.cutom_font')[0].text.encode('utf-8')
    salary = salary.replace(b'\xee\xb4\x82',b'2')
    salary = salary.replace(b'\xee\x91\xb9',b'0')
    salary = salary.replace(b'\xee\x8e\x92',b'3')
    salary = salary.replace(b'\xee\x8d\x8e',b'1')
    salary = salary.replace(b'\xee\x8b\x92',b'5')
    salary = salary.replace(b'\xef\x90\x80',b'8')
    salary = salary.replace(b'\xee\x94\x8d',b'4')
    salary = salary.decode('utf-8')

    print(salary)
    print(title,'-->',com_name)

def parse_html(html):
    bs = BeautifulSoup(html,'lxml')
    offers = bs.select('.intern-wrap.intern-item')
    # print(offers[0])
    for offer in offers:
        url = offer.select('.f-l.intern-detail__job a')[0]['href']
        # print(url)
        detail_url(url)

if __name__ == '__main__':
    html = send_request()
    parse_html(html)
Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐