编程杂谈

爬取难点HTML的canvas标签生成的图片,使用平常获取图片URL下载是行不通的。说明爬虫完整代码https://pan.bigdataboy.cn/#/s/wjHw网站csTimer-魔方竞速训练专用计时器爬取内容模仿公式公式对应的canvas图片工具选取Pythonselenium开始爬取爬取canvas的思路是执行canvas.toDataURL("image/png")方法获取图片的base64编码,再转码为bytes,然后保存在本地构造主类classMain:#图片保存路径IMG_PATH="./img/"#公式保存文件FILE="a.txt"#保存公式的个数COUNT=5000#保存速度SPEED=0.1#None:无限制。支持浮点数def__init__(self):#TODO打开网站defsave(self,i):#TODO保存公式及图片defrun(self):#TODO运行类构造类方法def__init__(self):options=ChromeOptions()options.add_experimental_option('excludeSwitches',['enable-automation'])#隐藏自动化测试#options.add_argument("--headless")#注释掉,就无需打开浏览器窗口#加载网站self.bro=webdriver.Chrome(executable_path="./chromedriver_win32/chromedriver.exe",options=options)self.bro.get(url="https://www.cstimer.net/")#检查网页是否加载完成WebDriverWait(self.bro,60).until(EC.presence_of_element_located((By.XPATH,'//div[@id="leftbar"]/div[@class="mybuttonc6"]/div/span[2]')))#判断图片框是否显示ifnotself.bro.find_element_by_id('toolsDiv').is_displayed():#点击显示图片self.bro.find_element_by_xpath('//div[@id="leftbar"]/div[@class="mybuttonc6"]/div/span[2]').click()保存类方法defsave(self,i):#保存公式formula=self.bro.find_element_by_xpath('//div[@id="scrambleTxt"]/div').textwithopen(self.FILE,mode='a+',encoding="UTF-8")asf:text=f"{formula}----{i}\n"logger.info(text)f.write(text)#保存图片JS='returndocument.getElementById("toolsDiv").childNodes[0].childNodes[0].childNodes[0].toDataURL("image/png");'img_base64=self.bro.execute_script(JS).split(',')[1]#执行js文件得到带图片的base64编码img_bytes=base64.b64decode(img_base64)#转为bytes类型path=f"{self.IMG_PATH}{i}.png"logger.info(path)withopen(path,'wb')asf:#保存图片到本地f.write(img_bytes)运行类方法defrun(self):i=1whileself.COUNT>=i:self.bro.find_element_by_xpath('//div[@class="title"]/nobr[2]/span[2]').click()self.save(i)ifself.SPEED:sleep(self.SPEED)logger.info(f"延迟:{str(self.SPEED)}中....")i+=1logger.info(f"爬取完成")使用if__name__=='__main__':mian=Main()mian.run()

2020-9-10 626 0
编程杂谈

if判断语句单条件判断inti=10;if(i<18){printf("你还小呢!!!\n");}else{printf("还行满18岁了\n");};多条件判断inti=10;if(i<18){printf("你还小呢!!!\n");}elseif(i==18){printf("才刚刚18岁\n");}else{printf("嗯已经大于18岁了\n");};if嵌套inti=10;if(i<18){printf("你还小呢!!!\n");}elseif(i>=18){//嵌套ifif(i==18){printf("刚刚18岁\n");}else{printf("不错已经成年了\n");};};swich匹配语句break;表示跳出该语句+,不然会继续执行后面的匹配块的语句。charsing='A';switch(sign){//语句块case'A':{printf("%c级不错\n",sign);};break;//单行语句case'B':printf("%c级还行\n",sign);break;//没有匹配到的默认语句default:printf("没有这个评级\n");break;};while语句统计用户输入字符的个数#include<stdio.h>intmain(){printf("输入一串字符:");//第一次调用getchar()会等待用户输入内容,存入缓冲区getchar();intcount=1;/*再次调用,这不会等待用户输入内容而是从缓冲区读取内容,一次读取一个字符*/while(getchar()!='\n'){count=count+1;};printf("你输入了%d个字符\n",count);//当缓冲区读取完成时,再一次调用,又会等待用户输入getchar();return0;}dowhile语句先执行,再判断do{循环体;}while(判断语句);for循环intcount;for(count=0;count<0;count++){printf("循环体")}//c99标准可以在表达式定义变量//gcc-std=c99for(intcount=0;count<0;count++){printf("循环体")}灵活的for循环for循环的表达式可以按照需要进行省略,但是分号不能省略for(表达式1;表达式2;表达式3){}for(;表达式2;表达式3){}for(表达式1;;表达式3){}for(表达式1;表达式2;){}for(;;){}//这个相当于死循环break结束循环,相当于跳到循环尾continue跳过本次循环

Python极客

日志级别级别说明级别何时使用DEBUG细节信息,仅当诊断问题时适用。INFO确认程序按预期运行WARNING表明有已经或即将发生的意外(例如:磁盘空间不足)。程序仍按预期进行ERROR由于严重的问题,程序的某些功能已经不能正常执行CRITICAL严重的错误,表明程序已不能继续执行级别等级默认等级是WARNING,这意味着仅仅这个等级及以上的才会反馈信息,除非logging模块被用来做其它事情。级别数字值CRITICAL50ERROR40WARNING30INFO20DEBUG10NOTSET0中间处理器(Handler)是配置日志是打印在控制台,还是输出到文件等等,多个处理器可以共用logging.FileHandler该类会把日志写入磁盘文件importloggingfromloggingimportFileHandler#返回一个指定记录器名称logger=logging.getLogger(__name__)#该中间程序器会把日志写入磁盘文件handler=FileHandler(filename="error.log",#日志文件名称mode='a',#写入模式encoding="utf-8"#编码)#该中间程序处理器的日志级别handler.setLevel(logging.ERROR)#设置该中间处理器的日志输出格式handler.setFormatter(logging.Formatter('%(asctime)-15s%(levelname)s%(filename)s%(lineno)d%(process)d%(message)s'))#添加中间处理器logger.addHandler(handler)#使用logger.error(msg=f'文件输出')logging.StreamHandler控制台输出importloggingfromloggingimportStreamHandler#返回一个指定记录器名称logger=logging.getLogger(__name__)#该中间程序器会把日志写入到流中handler=StreamHandler()#该中间程序处理器的日志级别handler.setLevel(logging.ERROR)#设置该中间处理器的日志输出格式handler.setFormatter(logging.Formatter('%(asctime)-15s%(levelname)s%(filename)s%(lineno)d%(process)d%(message)s'))#添加中间处理器logger.addHandler(handler)#使用logger.error(msg=f'文件输出')日志输入格式配置(Formatter)规定日志输出的内容的格式格式描述%(levelno)s打印日志级别的数值%(levelname)s日志级别%(pathname)s当前执行程序的路径%(filename)s当前执行程序名称%(funcName)s日志的当前函数%(lineno)d日志的当前行号%(asctime)s日志的时间%(thread)d线程id%(threadName)s线程名称%(process)d进程ID%(message)s日志信息logging.Formatter('%(asctime)-15s%(levelname)s%(filename)s%(lineno)d%(process)d%(message)s')----2020-08-1017:20:50,687ERRORtest.py2233480文件输出常见配置单个文件使用默认输出到控制台importlogginglogging.basicConfig(level=logging.INFO,format='%(asctime)s-%(name)s-%(levelname)s-%(message)s')logger=logging.getLogger(__name__)logger.info(msg=f"大数据男孩")输出结果2020-08-1017:48:30,772-__main__-INFO-大数据男孩多个文件应用importloggingimportsysfromosimportmakedirsfromos.pathimportdirname,existsloggers={}LOG_ENABLED=True#是否开启日志LOG_TO_CONSOLE=True#是否输出到控制台LOG_TO_FILE=True#是否输出到文件LOG_TO_ES=True#是否输出到ElasticsearchLOG_PATH='./runtime.log'#日志文件路径LOG_LEVEL='DEBUG'#日志级别LOG_FORMAT='%(levelname)s-%(asctime)s-process:%(process)d-%(filename)s-%(name)s-%(lineno)d-%(module)s-%(message)s'#每条日志输出格式defget_logger(name=None):"""getloggerbyname:paramname:nameoflogger:return:logger"""globalloggersifnotname:name=__name__ifloggers.get(name):returnloggers.get(name)logger=logging.getLogger(name)logger.setLevel(LOG_LEVEL)#输出到控制台ifLOG_ENABLEDandLOG_TO_CONSOLE:stream_handler=logging.StreamHandler(sys.stdout)stream_handler.setLevel(level=LOG_LEVEL)formatter=logging.Formatter(LOG_FORMAT)stream_handler.setFormatter(formatter)logger.addHandler(stream_handler)#输出到文件ifLOG_ENABLEDandLOG_TO_FILE:#如果路径不存在,创建日志文件文件夹log_dir=dirname(LOG_PATH)ifnotexists(log_dir):makedirs(log_dir)#添加FileHandlerfile_handler=logging.FileHandler(LOG_PATH,encoding='utf-8')file_handler.setLevel(level=LOG_LEVEL)formatter=logging.Formatter(LOG_FORMAT)file_handler.setFormatter(formatter)logger.addHandler(file_handler)#保存到全局loggersloggers[name]=loggerreturnloggerif__name__=='__main__':logger=get_logger()logger.debug('thisisamessage')输出结果DEBUG-2020-08-1017:46:12,213-process:17884-demo.py-__main__-59-demo-thisisamessage

编程杂谈

说明主要使用语言&框架:Python&Scrapy源码地址:https://pan.bigdataboy.cn/#/s/6euy爬取思路第一次搜索:获得所有页数url(简化)'https://search.jd.com/Search?keyword={}'methodGETheaders{'user-agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/84.0.4147.105Safari/537.36'}直接请求商品接口,获取商品id&价格url(简化)'https://search.jd.com/s_new.php?keyword={搜索词}&page={页数}&s={分页数}'methodGETheaders{'referer':'https://search.jd.com/Search'}使用商品id,请求商品页面,获得相关信息urlhttps://item.jd.com/{商品id}.htmlmethodGET请求评论接口,获取评论相关数量url)'https://club.jd.com/comment/productCommentSummaries.action?referenceIds={商品id}methodGETheaders{'referer':'https://search.jd.com/Search'}编写爬虫常规设置创建工程&爬虫文件scrapystartprojectjdAppcdjdAppscrapygenspiderjdspiderjd.com修改setting.py文件#伪装#Crawlresponsiblybyidentifyingyourself(andyourwebsite)ontheuser-agentUSER_AGENT='Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/689.36(KHTML,likeGecko)Chrome/99.0.5204.108Safari/637.75'#Obeyrobots.txtrulesROBOTSTXT_OBEY=False#下载延迟DOWNLOAD_DELAY=1#Overridethedefaultrequestheaders:DEFAULT_REQUEST_HEADERS={'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'en','referer':'https://search.jd.com/Search',#京东会检测这个}总体架构classJdspiderSpider(scrapy.Spider):name='jdspider'allowed_domains=['jd.com']#爬取的商品good_name=""#第一次请求获取全部商品全部页数search_url='https://search.jd.com/Search?keyword={}'#第一次搜索接口headers={'user-agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/84.0.4147.105Safari/537.36'}#获取商品接口每次请求返回30个goods_url_api='https://search.jd.com/s_new.php?keyword={}&page={}&s={}'#搜索接口#获取页数请求每一页defstart_requests(self):pass#获取每一个商品id,请求商品详情数据defparse(self,response:Response):pass#获取商品详情数据,请求评论信息defget_infor(self,response:Response):pass#得到评论信息,返回itemdefget_comment(self,response:Response):passif__name__=='__main__':#启动fromscrapyimportcmdlinecmdline.execute("scrapyrunspiderjdspider.py".split())start_requests(self)#获取页数请求每一页defstart_requests(self):self.good_name=input("输入爬取的关键词:")r=requests.get(url=self.search_url.format(self.good_name),headers=self.headers)#获取总页数all_pages=re.search(r'<b>1</b><em>/</em><i>(\d*?)</i>',r.text).group(1)logger.info(msg=f"总页数{all_pages}")for_inrange(int(all_pages)*2):#组合每一页的链接goods_url_api=self.goods_url_api.format(self.good_name,_+1,1+25*(_+1-1))logger.info(msg=f"请求链接:{goods_url_api}")#请求每一页的商品yieldRequest(url=goods_url_api,callback=self.parse)parse(self,response:Response)#获取每一个商品id,请求商品详情数据defparse(self,response:Response):item=JdappItem()drug_list:list[Selector]=response.xpath("//div[@id='J_goodsList']/ul/li")for_indrug_list:#商品价格item['good_price']=_.xpath('//div[@class="p-price"]/strong/i/text()').get()#商品iditem['good_id']=_.xpath("@data-sku").get()url=f'https://item.jd.com/{item["good_id"]}.html'logger.info(msg=f"正在爬取商品详情:{url}")yieldRequest(url=url,meta={'item':copy.deepcopy(item),'url':url},callback=self.get_infor,)get_infor(self,response:Response)#获取商品详情数据,请求评论信息defget_infor(self,response:Response):item=response.meta['item']#商品连接item['good_url']=response.urlinfor=dict()#商品介绍parameter:list[str]=response.xpath('//div[@class="p-parameter"]/ul[2]/li/text()').getall()for_inparameter:i=_.split(":")infor[i[0]]=i[1]#规格与包装size_packing_key:list[str]=response.xpath('//div[@class="Ptable"]/div/dl/dl/dt/text()').getall()size_packing_value:list[str]=response.xpath('//div[@class="Ptable"]/div/dl/dl/dd/text()').getall()for_inzip(size_packing_key,size_packing_value):infor[_[0]]=_[1]logger.info(msg=f"商品参数规格信息{infor}")#商品信息item['good_infor']=infor#获取评论url=f'https://club.jd.com/comment/productCommentSummaries.action?referenceIds={item["good_id"]}'logger.info(msg=f"正在爬取商品评论{url}")yieldRequest(url=url,meta={'item':copy.deepcopy(item)},callback=self.get_comment,dont_filter=True)get_comment(self,response:Response)#得到评论信息,返回itemdefget_comment(self,response:Response):item=response.meta['item']comment=json.loads(response.text)#评论数item['comment_number']=comment.get('CommentsCount')[0].get('CommentCountStr')#好评item['good_comment']=comment.get('CommentsCount')[0].get('GoodCountStr')#中评item['medium_comment']=comment.get('CommentsCount')[0].get('GeneralCountStr')#差评item['bad_comment']=comment.get('CommentsCount')[0].get('PoorCountStr')yielditemtip在爬取商品详情页时,需要经常更换IP,不要容易什么都获取不到,源码里的动态转发需要更改为自己的秘钥和订单注意每个Request请求meta的传递方式

编程杂谈

说明项目完整代码:https://pan.bigdataboy.cn/#/s/ZjSy爬虫难点可以发现所需要提交的参数是被加密的(虽然这段加密结果每次都不一样,可以重复使用,但这不是我们的目的,我们并不仅限于一个歌单)开始分析定位Js加密函数位置搜索,分析加密参数所在的Js文件定位加密函数所在位置分析加密参数定位到了加密参数所在的位置,就需要分析传入的参数通过多次抓包,发现这三个参数都是定值bqN1x(["流泪","强"])->010001bqN1x(["爱心","女孩","惊恐","大笑"])->0CoJUm6Qyw8W8judbqN1x(Wx4B.md)->00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b725152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbda92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe4875d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7继续查看传入的第一个参数,找到真正的参数列表{csrf_token:"",cursor:"-1",offset:"0",orderType:"1",pageNo:pageNo,pageSize:pageSize,rid:"A_PL_0_"+rid,threadId:"A_PL_0_"+rid,}分析加密函数进入加密函数内部查看分析发现:需要的参数列表被加密进了变量params,所以有个取巧(不用管变量encSecKey,把他当做一个常量)实现加密过程到了这里差不多就了解了大概的加密过程,所以我就需要选着实现加密过程的方式,这里有两种方式可供查考使用Python重写加密过程(加密简单还能实现,费时费力)使用Python执行Js代码,得到加密结果(比较推荐)这里使用execjs(注意安装的时候是pyexecjs),它会自动调动本机的Js环境执行代码(本机已安装node.js。如果Js加密代码里使用了windows对象,document对象获取参数…就需要选择其他能够调用浏览器驱动的类库)构建加密Js文件先把主要调用的Js代码复制出来使用execjs执行这段Js代码importexecjs#读取Js文件js=open('music.163.js','r',encoding="utf-8").read()#预编译Js文件ext=execjs.compile(js)#执行Js函数result=ext.call('start')print(result)说明所复制的Js代码不够完整,所以就需要继续去找,然后添加到Js文件里。执行加密Js代码importrequestsimportexecjsurl='https://music.163.com/weapi/comment/resource/comments/get?csrf_token='headers={'user-agent':'Mozilla/5.0(WindowsNT10.0;Win64;x64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/84.0.4147.105Safari/537.36',}#读取Js文件js=open('music.163.js','r',encoding="utf-8").read()#预编译Js文件&执行Js函数result=execjs.compile(js).call('start','3130270730')data={'params':result['encText'],'encSecKey':result['encSecKey']}#发送请求r=requests.post(url=url,headers=headers,data=data)print(r.json())print(r)彩蛋:网易音乐的大部分接口都是这个加密方式,只是提交的参数列表不一样

Python极客

说明现在网上关于FastAPI的项目结构的资料比较少,以下仅供参考,如有更好的想法,可私聊博主交流项目下载:https://pan.bigdataboy.cn/#/s/oWsb结构说明:项目结构的静态文件使用Jinja2渲染项目├──modules(功能模块)│├──│└──init.py├──routers(路由控制)│├──index.py│├──items.py│├──users.py│└──init.py├──static(静态文件)│├──css│└──js├──templates(网页文件)│└──index.html├──init.py├──app.py(主文件)├──public.py(公共方法)└──config.py(配置文件)结构说明routers(路由控制)控制路由的响应(静态文件,还是Json,…)响应静态文件html.pyfromfastapiimportAPIRouter,HTTPExceptionfromstarlette.templatingimportJinja2Templatesfromstarlette.requestsimportRequestrouter=APIRouter()#绑定模板路径template=Jinja2Templates(directory="templates")@router.get("/")asyncdefindex(request:Request):#request为必传,后面的就跟Jinja2一样returntemplate.TemplateResponse('index.html',{'request':request,"user":"啦啦啦啦"})接口响应路由users.py。/users/路径在本文件定义fromfastapiimportAPIRouterrouter=APIRouter()@router.get("/users/",tags=["users"])asyncdefread_users():return[{"username":"Foo"},{"username":"Bar"}]@router.get("/users/me",tags=["users"])asyncdefread_user_me():return{"username":"fakecurrentuser"}@router.get("/users/{username}",tags=["users"])asyncdefread_user(username:str):return{"username":username}接口响应路由items.py。/items/路径在app.py定义fromfastapiimportAPIRouter,HTTPExceptionrouter=APIRouter()@router.get("/")asyncdefuse_api_munber():return[{"name":"ItemFoo"},{"name":"itemBar"}]@router.get("/{item_id}")asyncdefread_item(item_id:str):return{"name":"FakeSpecificItem","item_id":item_id}@router.put("/{item_id}",tags=["custom"],responses={403:{"description":"Operationforbidden"}},)asyncdefupdate_item(item_id:str):ifitem_id!="foo":raiseHTTPException(status_code=403,detail="Youcanonlyupdatetheitem:foo")return{"item_id":item_id,"name":"TheFighters"}app.py(主文件)fromfastapiimportDepends,FastAPIfromFastAPI_Project.routersimporthtml,items,usersfrompublicimportcount_usefromfastapi.staticfilesimportStaticFilesimportconfigimportuvicornapp=FastAPI(debug=True,version=config.VERSION,)#静态文件绑定app.mount('/static',StaticFiles(directory='static'),name='static')#静态文件路由app.include_router(html.router)#API路由app.include_router(items.router,prefix="/items",#添加路由路径tags=["items"],#标签路由分组作用#注入依赖请求这个路由之前,这个依赖的方法可以用于验证统计等dependencies=[Depends(count_use)],)app.include_router(users.router,dependencies=[Depends(count_use)],responses={404:{"description":"Notfound"}},)if__name__=='__main__':uvicorn.run(app=app,host=config.HOST,port=config.PORT,)config.py配置文件配置参数,链接数据什么的importpymongo#通用配置classConfig:#MongoDB连接地址MONGODB=pymongo.MongoClient("mongodb://localhost:27017/")DATABASES="bigdataboy"pass#运行地址HOST="127.0.0.1"#运行端口PORT=8686#版本VERSION="1.0.1"public.py(公共方法)fromfastapi.loggerimportloggerfromfastapi.requestsimportRequestfromtimeimporttimefromFastAPI_Project.configimportConfig"""记录接口使用信息"""asyncdefcount_use(r:Request):#获取api的路径path=r.url.path.strip("/").split("/")[0]#获取客户端ipip=r.client.host#获取客户端UAuser_agent=r.headers.getlist("user-agent")[-1]logger.info(msg=f"{path}{ip}{user_agent}")#插入mongodbConfig.MONGODB[Config.DATABASES]['count_use_api'].insert({"path":path,#接口路径"ip":ip,#客户端ip"user_agent":user_agent,#客户端UA"timestamp":time()#时间戳})

编程杂谈

算数运算符双目:1+2这样操作两个数的运算符单目:-1这样表示的是负一,只操作一个数运算符名称+加法运算符(双目)-减法运算符(双目)*乘法运算符(双目)/除法运算符(双目)%求余运算符(双目)+正号运算符(单目)-负号运算符(单目)类型转换写法:(int)3.9这是强制转换#include<stdio.h>intmain(){//类型转换printf("转换类型%d\n",(int)3.9);}关系运算符关系运算符都是双目运算符在C语言中的关系运算符的优先级大小关系表达式#include<stdio.h>intmain(){inta=1<2;printf("%d\n",a);return0;}逻辑运算符运算符含义优先级举例说明!非高!a如果a为真,!a就为假;反之&&与中a&&ba,b同为真才为真;a,b有一个假就为假;ll或低allba,b同为假才为假;a,b有一个真就为真;逻辑表达式#include<stdio.h>intmain(){printf("结果%d\n",3>1&&1<2);printf("结果%d\n",3+1||2==0);printf("结果%d\n",!('a'+'b'));printf("结果%d\n",!0+1<1||!(3+4));return0;}短路运算(典型逻辑运算)#include<stdio.h>intmain(){inta=3,b=5;(a=0)&&(b=3);printf("a=%d,b=%d\n",a,b);(a=1)||(b=4);printf("a=%d,b=%d\n",a,b);return0;}运行结果a=0,b=5a=1,b=5解释#include<stdio.h>intmain(){inta=3,b=5;//与运算,(a=0)为假,不管后面是什么结果都是假,所以就不会向后执行了(a=0)&&(b=3);printf("a=%d,b=%d\n",a,b);//非运算,(a=1)为真,后面是什么都不影响结果的真,所以也不会往后运算了(a=1)||(b=4);printf("a=%d,b=%d\n",a,b);return0;}

2020-8-7 699 0
2020-7-27 716 1