Python3 爬虫实战 — 模拟登陆哔哩哔哩【滑动验证码对抗】


  • 登陆时间:2019-10-21
  • 实现难度:★★★☆☆☆
  • 请求链接:https://passport.bilibili.com/login
  • 实现目标:模拟登陆哔哩哔哩,攻克滑动验证码
  • 涉及知识:滑动验证码的攻克、自动化测试工具 Selenium 的使用
  • 完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/AutomationTool/bilibili-login
  • 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice
  • 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278

文章目录

  • 【1x00】思维导图
  • 【2x00】登陆模块
    • 【2x01】初始化函数
    • 【2x02】登陆函数
  • 【3x00】验证码处理模块
    • 【3x01】验证码元素查找函数
    • 【3x02】元素可见性设置函数
    • 【3x03】验证码截图函数
  • 【4x00】验证码滑动模块
    • 【4x01】滑动主函数
    • 【4x02】缺口位置寻找函数
    • 【4x03】计算滑块移动距离函数
    • 【4x04】构造移动轨迹函数
    • 【4x05】模拟拖动函数
  • 【5x00】完整代码
  • 【6x00】效果实现动图


【1x00】思维导图

01

  • 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证

  • 分析页面,想办法找到滑动验证码的完整图片、带有缺口的图片和需要滑动的图片

  • 对比原始的图片和带缺口的图片的像素,像素不同的地方就是缺口位置

  • 计算出滑块缺口的位置,得到所需要滑动的距离

  • 拖拽时要模仿人的行为,由于有个对准过程,所以要构造先快后慢的运动轨迹

  • 最后利用 Selenium 进行对滑块的拖拽

02


【2x00】登陆模块

【2x01】初始化函数

def init():global url, browser, username, password, waiturl = 'https://passport.bilibili.com/login'# path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径path = r'F:\PycharmProjects\Python3爬虫\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)# 你的哔哩哔哩用户名username = '155********'# 你的哔哩哔哩登陆密码password = '***********'wait = WebDriverWait(browser, 20)

global 关键字定义了发起请求的url、用户名、密码等全局变量,随后是登录页面url、谷歌浏览器驱动的目录path、实例化 Chrome 浏览器、设置浏览器分辨率最大化、用户名、密码、WebDriverWait() 方法设置等待超时


【2x02】登陆函数

def login():browser.get(url)# 获取用户名输入框user = wait.until(EC.presence_of_element_located((By.ID, 'login-username')))# 获取密码输入框passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))# 输入用户名user.send_keys(username)# 输入密码passwd.send_keys(password)# 获取登录按钮login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login')))# 随机暂停几秒time.sleep(random.random() * 3)# 点击登陆按钮login_btn.click()

等待用户名输入框和密码输入框对应的 ID 节点加载出来

获取这两个节点,用户名输入框 id="login-username",密码输入框 id="login-passwd"

调用 send_keys() 方法输入用户名和密码

获取登录按钮 class="btn btn-login"

随机产生一个数并将其扩大三倍作为暂停时间

最后调用 click() 方法实现登录按钮的点击


【3x00】验证码处理模块

【3x01】验证码元素查找函数

def find_element():# 获取带有缺口的图片c_background = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute')))# 获取需要滑动的图片c_slice = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute')))# 获取完整的图片c_full_bg = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))# 隐藏需要滑动的图片hide_element(c_slice)# 保存带有缺口的图片save_screenshot(c_background, 'back')# 显示需要滑动的图片show_element(c_slice)# 保存需要滑动的图片save_screenshot(c_slice, 'slice')# 显示完整的图片show_element(c_full_bg)# 保存完整的图片save_screenshot(c_full_bg, 'full')

获取验证码的三张图片,分别是完整的图片、带有缺口的图片和需要滑动的图片

分析页面代码,三张图片是由 3 个 canvas 组成,3 个 canvas 元素包含 CSS display 属性,display:block 为可见,display:none 为不可见,在分别获取三张图片时要将其他两张图片设置为 display:none,这样做才能单独提取到每张图片

定位三张图片的 class 分别为:带有缺口的图片(c_background):geetest_canvas_bg geetest_absolute、需要滑动的图片(c_slice):geetest_canvas_slice geetest_absolute、完整图片(c_full_bg):geetest_canvas_fullbg geetest_fade geetest_absolute

最后传值给 save_screenshot() 函数,进一步对验证码进行处理

03


【3x02】元素可见性设置函数

# 设置元素不可见
def hide_element(element):browser.execute_script("arguments[0].style=arguments[1]", element, "display: none;")# 设置元素可见
def show_element(element):browser.execute_script("arguments[0].style=arguments[1]", element, "display: block;")

【3x03】验证码截图函数

def save_screenshot(obj, name):try:# 首先对出现验证码后的整个页面进行截图保存pic_url = browser.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)# 计算传入的obj,也就是三张图片的位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']# 打印输出一下每一张图的位置信息print('图:' + name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')# 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))file_name = 'bili_' + name + '.png'im.save(file_name)except BaseException as msg:print("%s:截图失败!" % msg)

location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x轴向右递增,y轴向下递增

size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息

首先调用 save_screenshot() 属性对整个页面截图并保存

然后向 crop() 方法传入验证码的位置信息,由位置信息再对验证码进行剪裁并保存


【4x00】验证码滑动模块

【4x01】滑动主函数

def slide():distance = get_distance(Image.open('.\\bili_back.png'), Image.open('.\\bili_full.png'))print('计算偏移量为:%s Px' % distance)trace = get_trace(distance - 5)move_to_gap(trace)time.sleep(3)

get_distance() 函数传入完整的图片和缺口图片,计算滑块需要滑动的距离,再把距离信息传入 get_trace() 函数,构造滑块的移动轨迹,最后根据轨迹信息调用 move_to_gap() 函数移动滑块完成验证


【4x02】缺口位置寻找函数

def is_pixel_equal(bg_image, fullbg_image, x, y):# 获取两张图片对应像素点的RGB数据bg_pixel = bg_image.load()[x, y]fullbg_pixel = fullbg_image.load()[x, y]# 设定一个阈值threshold = 60# 比较两张图 RGB 的绝对值是否均小于定义的阈值if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):return Trueelse:return False

将完整图片和缺口图片两个对象分别赋值给变量 bg_imagefullbg_image,接下来对比图片获取缺口。遍历图片的每个坐标点,获取两张图片对应像素点的 RGB 数据,判断像素的各个颜色之差,abs() 用于取绝对值,比较两张图 RGB 的绝对值是否均小于定义的阈值 threshold,如果绝对值均在阈值之内,则代表像素点相同,继续遍历,否则代表不相同的像素点,即缺口的位置


【4x03】计算滑块移动距离函数

def get_distance(bg_image, fullbg_image):# 滑块的初始位置distance = 60# 遍历两张图片的每个像素for i in range(distance, fullbg_image.size[0]):for j in range(fullbg_image.size[1]):# 调用缺口位置寻找函数if not is_pixel_equal(fullbg_image, bg_image, i, j):return i

get_distance() 方法即获取缺口位置的方法,此方法的参数是两张图片,一张为完整的图片,另一张为带缺口的图片,distance 为滑块的初始位置,遍历两张图片的每个像素,利用 is_pixel_equal() 缺口位置寻找函数判断两张图片同一位置的像素是否相同,若不相同则返回该点的值


【4x04】构造移动轨迹函数

def get_trace(distance):trace = []# 设置加速距离为总距离的4/5faster_distance = distance * (4 / 5)# 设置初始位置、初始速度、时间间隔start, v0, t = 0, 0, 0.1while start < distance:if start < faster_distance:a = 10else:a = -10# 位移move = v0 * t + 1 / 2 * a * t * t# 当前时刻的速度v = v0 + a * tv0 = vstart += movetrace.append(round(move))# trace 记录了每个时间间隔移动了多少位移return trace

get_trace() 方法传入的参数为移动的总距离,返回的是运动轨迹,运动轨迹用 trace 表示,它是一个列表,列表的每个元素代表每次移动多少距离,利用 Selenium 进行对滑块的拖拽时要模仿人的行为,由于有个对准过程,所以是先快后慢,匀速移动、随机速度移动都不会成功,因此要设置一个加速和减速的距离,这里设置加速距离 faster_distance 是总距离 distance 的4/5倍,滑块滑动的加速度用 a 来表示,当前速度用 v 表示,初速度用 v0 表示,位移用 move 表示,所需时间用 t 表示,它们之间满足以下关系:

move = v0 * t + 0.5 * a * t * t 
v = v0 + a * t

设置初始位置、初始速度、时间间隔分别为0, 0, 0.1,加速阶段和减速阶段的加速度分别设置为10和-10,直到运动轨迹达到总距离时,循环终止,最后得到的 trace 记录了每个时间间隔移动了多少位移,这样滑块的运动轨迹就得到了


【4x05】模拟拖动函数

def move_to_gap(trace):# 获取滑动按钮slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button')))# 点击并拖动滑块ActionChains(browser).click_and_hold(slider).perform()# 遍历运动轨迹获取每小段位移距离for x in trace:# 移动此位移ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()time.sleep(0.5)# 释放鼠标ActionChains(browser).release().perform()

传入的参数为运动轨迹,首先查找到滑动按钮,然后调用 ActionChains 的 click_and_hold() 方法按住拖动底部滑块,perform() 方法用于执行,遍历运动轨迹获取每小段位移距离,调用 move_by_offset() 方法移动此位移,最后调用 release() 方法松开鼠标即可


【5x00】完整代码

# =============================================
# --*-- coding: utf-8 --*--
# @Time    : 2019-10-21
# @Author  : TRHX
# @Blog    : www.itrhx.com
# @CSDN    : https://blog.csdn.net/qq_36759224
# @FileName: bilibili.py
# @Software: PyCharm
# =============================================from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import time
import random
from PIL import Image# 初始化函数
def init():global url, browser, username, password, waiturl = 'https://passport.bilibili.com/login'# path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径path = r'F:\PycharmProjects\Python3爬虫\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)# 你的哔哩哔哩用户名username = '155********'# 你的哔哩哔哩登录密码password = '***********'wait = WebDriverWait(browser, 20)# 登录函数
def login():browser.get(url)# 获取用户名输入框user = wait.until(EC.presence_of_element_located((By.ID, 'login-username')))# 获取密码输入框passwd = wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))# 输入用户名user.send_keys(username)# 输入密码passwd.send_keys(password)# 获取登录按钮login_btn = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'a.btn.btn-login')))# 随机暂停几秒time.sleep(random.random() * 3)# 点击登陆按钮login_btn.click()# 验证码元素查找函数
def find_element():# 获取带有缺口的图片c_background = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_bg.geetest_absolute')))# 获取需要滑动的图片c_slice = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_slice.geetest_absolute')))# 获取完整的图片c_full_bg = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'canvas.geetest_canvas_fullbg.geetest_fade.geetest_absolute')))# 隐藏需要滑动的图片hide_element(c_slice)# 保存带有缺口的图片save_screenshot(c_background, 'back')# 显示需要滑动的图片show_element(c_slice)# 保存需要滑动的图片save_screenshot(c_slice, 'slice')# 显示完整的图片show_element(c_full_bg)# 保存完整的图片save_screenshot(c_full_bg, 'full')# 设置元素不可见
def hide_element(element):browser.execute_script("arguments[0].style=arguments[1]", element, "display: none;")# 设置元素可见
def show_element(element):browser.execute_script("arguments[0].style=arguments[1]", element, "display: block;")# 验证码截图函数
def save_screenshot(obj, name):try:# 首先对出现验证码后的整个页面进行截图保存pic_url = browser.save_screenshot('.\\bilibili.png')print("%s:截图成功!" % pic_url)# 计算传入的obj,也就是三张图片的位置信息left = obj.location['x']top = obj.location['y']right = left + obj.size['width']bottom = top + obj.size['height']# 打印输出一下每一张图的位置信息print('图:' + name)print('Left %s' % left)print('Top %s' % top)print('Right %s' % right)print('Bottom %s' % bottom)print('')# 在整个页面截图的基础上,根据位置信息,分别剪裁出三张验证码图片并保存im = Image.open('.\\bilibili.png')im = im.crop((left, top, right, bottom))file_name = 'bili_' + name + '.png'im.save(file_name)except BaseException as msg:print("%s:截图失败!" % msg)# 滑动模块的主函数
def slide():distance = get_distance(Image.open('.\\bili_back.png'), Image.open('.\\bili_full.png'))print('计算偏移量为:%s Px' % distance)trace = get_trace(distance - 5)move_to_gap(trace)time.sleep(3)# 计算滑块移动距离函数
def get_distance(bg_image, fullbg_image):# 滑块的初始位置distance = 60# 遍历两张图片的每个像素for i in range(distance, fullbg_image.size[0]):for j in range(fullbg_image.size[1]):# 调用缺口位置寻找函数if not is_pixel_equal(fullbg_image, bg_image, i, j):return i# 缺口位置寻找函数
def is_pixel_equal(bg_image, fullbg_image, x, y):# 获取两张图片对应像素点的RGB数据bg_pixel = bg_image.load()[x, y]fullbg_pixel = fullbg_image.load()[x, y]# 设定一个阈值threshold = 60# 比较两张图 RGB 的绝对值是否均小于定义的阈值if (abs(bg_pixel[0] - fullbg_pixel[0] < threshold) and abs(bg_pixel[1] - fullbg_pixel[1] < threshold) and abs(bg_pixel[2] - fullbg_pixel[2] < threshold)):return Trueelse:return False# 构造移动轨迹函数
def get_trace(distance):trace = []# 设置加速距离为总距离的4/5faster_distance = distance * (4 / 5)# 设置初始位置、初始速度、时间间隔start, v0, t = 0, 0, 0.1while start < distance:if start < faster_distance:a = 10else:a = -10# 位移move = v0 * t + 1 / 2 * a * t * t# 当前时刻的速度v = v0 + a * tv0 = vstart += movetrace.append(round(move))# trace 记录了每个时间间隔移动了多少位移return trace# 模拟拖动函数
def move_to_gap(trace):# 获取滑动按钮slider = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'div.geetest_slider_button')))# 点击并拖动滑块ActionChains(browser).click_and_hold(slider).perform()# 遍历运动轨迹获取每小段位移距离for x in trace:# 移动此位移ActionChains(browser).move_by_offset(xoffset=x, yoffset=0).perform()time.sleep(0.5)# 释放鼠标ActionChains(browser).release().perform()if __name__ == '__main__':init()login()find_element()slide()

【6x00】效果实现动图

最终实现效果图:(关键信息已经过打码处理)
04

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437895.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】

登陆时间&#xff1a;2019-10-21实现难度&#xff1a;★★★☆☆☆请求链接&#xff1a;https://kyfw.12306.cn/otn/resources/login.html实现目标&#xff1a;模拟登陆中国铁路12306&#xff0c;攻克点触验证码涉及知识&#xff1a;点触验证码的攻克、自动化测试工具 Selenium…

SharePoint无代码工作流设计开发实例——交通费报销流程(一)

开发环境&#xff1a; (1)SharePoint Designer 2010 (2)SharePoint Online(Office 365) 关于Office 365和SharePoint Online的相关信息请参见以下链接 http://blog.csdn.net/miragesky2049/article/details/7242982 1、需求分析 组织结构&#xff1a; 用户类型&#xff1a…

Python3 爬虫实战 — 58同城武汉出租房【加密字体对抗】

爬取时间&#xff1a;2019-10-21爬取难度&#xff1a;★★★☆☆☆请求链接&#xff1a;https://wh.58.com/chuzu/爬取目标&#xff1a;58同城武汉出租房的所有信息涉及知识&#xff1a;网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代…

SharePoint无代码工作流设计开发实例——交通费报销流程(二)

3、InfoPath表单的设计及发布 (1)数据源和页面设计 域&#xff08;元素&#xff09;和域&#xff08;属性&#xff09;如下&#xff1a; 白色为域&#xff08;元素&#xff09;&#xff0c;该类型的字段&#xff0c;发布时&#xff0c;提升成字段后&#xff0c;列表库的数据是…

SharePoint无代码工作流设计开发实例——交通费报销流程(三)

4、SharePoint Designer无代码工作流的设计 开发工具为SharePoint Designer 2010 新建“列表工作流”&#xff0c;绑定“交通费报销单”表单库 设置工作流启动选项“创建项目时自动启动工作流” 编辑工作流 &#xff08;关于以下工作流详细设计可参考http://blog.csdn.net/mi…

SharePoint学习札记[2] — MOSS2007体系结构概述

熟悉微软Office产品线的人都知道&#xff0c;MOSS 2007的上一个版本SPS 2003的全称是 Office Sharepoint Portal Server&#xff0c;在新版本中&#xff0c;微软去掉了Portal一个词。从这个角度很清楚地看到微软对MOSS 2007的定位&#xff0c;是的&#xff0c;MOSS 已经不再仅仅…

SharePoint学习札记[3] — Office SharePoint Server 2007部署

MOSS的第一步就是部署啦!《0ffice SharePoint Server 2007部署图示指南》图文并茂的展示了OSS2007的部署过程。虽然并不难&#xff0c;但对MOSS还摸不清方向的人来说是个不错的指导。手册的作者没有署名&#xff0c;也查不到出自谁手&#xff0c;在这里对他的劳动成果表示感谢…

SharePoint学习札记[4] — 创建SharePoint站点

为减少创建SharePoint站点过程中的麻烦&#xff0c;先打开IIS管理器&#xff0c;将“默认站点”停止或删除。后面的创建过程中&#xff0c;创建向导会自动创建端口为80的网站。 现在将&#xff1a; 1. 创建两个Web应用程序。 2. 创建共享服务。 3. 创建…

【Python CheckiO 题解】Sun Angle

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

Python CheckiO 题解系列 丨 博客目录索引

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本题解系列主要记录自己在用 Python 闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 …

文带你深入了解 Redis 内存模型

前言 Redis是目前最火爆的内存数据库之一&#xff0c;通过在内存中读写数据&#xff0c;大大提高了读写速度&#xff0c;可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时&#xff0c;会接触Redis的5种对象类型&#xff08;字符串、哈希、列表、集合、有序集合…

【Python CheckiO 题解】Pawn Brotherhood

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

【Python CheckiO 题解】Xs and Os Referee

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

c#多线程总结(纯干货)

线程基础 创建线程 static void Main(string[] args) {Thread t new Thread(PrintNumbers);t.Start();//线程开始执行PrintNumbers();Console.ReadKey(); }static void PrintNumbers() {Console.WriteLine("Starting...");for (int i 1; i < 10; i){Console.Wr…

Hexo 博客优化之实用功能添加系列(持续更新)

2022-01-25 更新&#xff1a;博客新地址&#xff1a;https://www.itbob.cn/&#xff0c;文章距上次编辑时间较远&#xff0c;部分内容可能已经过时&#xff01; 本文将讲述一些 Hexo 博客实用功能的添加&#xff0c;本文以作者 luuman 的 spfk 主题和作者 xaoxuu 的 Material X…

【Python CheckiO 题解】The Warriors

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

【Python CheckiO 题解】Multiply (Intro)

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

关于DateTime的一点记录 ToString(yyyy-MM-dd HH:mm:ss)

DateTime dt DateTime.Now; string z dt.ToString("yyyy-MM-dd HH:mm:ss");//你知道这个是“年月日时分秒”的格式吧? string a dt.ToString("yyyy-MM-dd HH:mm:ss ms");//这个你认为一定是 毫秒的格式? string b dt.ToString("yyyy-MM-dd HH:…

SharePoint List item数量超过5000的解决办法

SharePoint一个list里面的item数量超过5000会提示“视图无法显示&#xff0c;因为超过管理员限制设定的列表视图阈值” 在CSDN里面有比较好的解决方案&#xff0c;在这里先记录下来&#xff0c;以后有用 方案一&#xff0c;定期自动归档 不用写TimerJOb&#xff0c; 可以用cont…

什么是SharePoint?

在聊SharePoint开发之前&#xff0c;有必要说下什么是SharePoint. 在我工作的过程中&#xff0c;经常遇到客户对SharePoint不太了解的情况。有客户说&#xff0c;SharePoint太烂了&#xff0c;DropBox能做到的什么什么功能&#xff0c;SharePoint竟然做不到&#xff0c;很明显…