说明主要使用语言&框架: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)彩蛋:网易音乐的大部分接口都是这个加密方式,只是提交的参数列表不一样
算数运算符双目: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;}
字符#include<stdio.h>intmain(){chara='A';//%c打印字符,%d打印数字printf("%c=%d\n",a,a);return0;}字符串在C语言中没有专门的关键字定义字符串,理解就是字符串也是由一串字符组成的‘字符串’写法#include<stdio.h>intmain(){charname[10];//开辟一个控件//给字符串赋值name[0]='b';name[1]='i';name[2]='g';name[3]='d';name[4]='a';name[5]='t';name[6]='a';name[7]='b';name[8]='o';name[9]='y';printf("%s\n",name);return0;}给字符串赋值的三种方式//先定义,再赋值charname[2];name[0]='b';name[1]='i';//直接赋值charname[10]={'b','i','g','d','a','t','a','d','o','y'};//如果字符串的末尾出现乱码,表示编译器没有识别到字符串的尾部,就需要自己加上`\0`charname[]={'b','i','g','d','a','t','a','d','o','y'};//不定义长度编译器会自动计算//直接定义字符串(注意使用双引号)charstr[]={"bigdataboy"};charstr[]="bigdataboy";
补码是什么一个数在计算机中是以二进制的形式存放的,补码的发明是为了表示带符号数(整型)进行方便的运算。补码的转换正是符号需要占有一格,所以带符号的整型的取值范围是-127~1277到-7的转换127、-127表示补码的用处出现的问题当计算机计算-7+7时,按照常识进行计算,底层是这样的:10000111+00000111=10001110,这时就发现并不等于0处理问题这时我们的前辈就想到的补码这种方式:-7二进制补码表示:111110017二进制表示:00000111进行加法运算:11111001+00000111=100000000结果是100000000但是只有8位去存储整数,所以第九位的1会溢出,这样显示的结果就是00000000,就完成我们需要的运算。扩展占用字节数越多,所能表示的数越大数据类型字节数取值范围char1-128~127unsignedchar10~255short2-32768~32767unsignedshort20~65535int4-2147483648~2147483647unsignedshort40~4294967295
数据类型基本类型整数类型:int浮点数类型:float字符类型:char布尔类型:_Bool枚举类型:enum构造类型数组类型结构类型联合类型指针类型空类型类型的长度在C语言中,并没有固定int的长度,如:16位编译器说明:int占16位,内存2字节,最大值:32767;32位和64位编译器:int占32位,内存4字节,最大值:21474836473位数的增加,那么自然位数也在倍增。整数类型长度大小:longlongint>=longint>=int>=shortint,C语言虽没有规定具体长度,但大小的一定是这样shortintintlongintlonglongint检测长度方法sizeof()sizeof(object);//对象sizeof(type_name);//类型sizeofobject;//对象#include<stdio.h>intmain(){printf("shortint的长度:%d\n",sizeof(shortint));printf("int的长度:%d\n",sizeof(int));printf("longint的长度:%d\n",sizeof(longint));printf("longlongint的长度:%d\n",sizeof(longlongint));return0;}浮点数类型&字符类型&布尔类型浮点数类型floatdoublelongdouble字符类型char布尔类型_Bool#include<stdio.h>intmain(){//整数类型printf("float的长度:%d\n",sizeof(float));printf("double的长度:%d\n",sizeof(double));printf("longdouble的长度:%d\n",sizeof(longdouble));//字符类型printf("char的长度:%d\n",sizeof(char));//布尔类型printf("_Bool的长度:%d\n",sizeof(_Bool));return0;}signed&unsigned有符号:能表示正负数;无符号:只能表示正数//定义例子-[singed]short[int]-unsignedshort[int]-[signed]int-unsignedint-[signed]long[int]-unsignedlong[int]-[signed]longlong[int]-unsignedlonglong[int]#include<stdio.h>intmain(){//有符号inti;//无符号unsignedintj;i=-520;j=520;printf("有符号%d\n",i);printf("无符号%u\n",j);//打印无符号的需要%u}
变量先定义,再赋值使用变量类型char:字符型,占用一个字节int:整形,通常反映了所用机器中整数的最自然长度float:单精度浮点型double:双精度浮点型…后面基本类型补充完整小例子#include<stdio.h>intmain(){//声明变量chara;intb;floatc;doubled;//变量赋值a='a';b=520;c=3.14;d=3.141592653;printf("char类型是%c\n",a);printf("我爱你%d\n",b);printf("圆周率是:%.2f\n",c);printf("圆周率后九位:%11.9f\n",d);return0;}常量定义后不能被改变,为了与变量区别,使用常量名使用大写常量的定义格式:#define常量名常量内容#include<stdio.h>//常量的定义#defineURL"https://bigdataboy.cn"intmain(){printf("大数据男孩网址:%s\n",URL);return0;}
C程序运行C语言的需要先编译成->汇编语言->机器语言,然后给CPU执行。C语言的优势灵活度高、可移植性高、效率高灵活度高:源代码最终编译成机器语言,也就是我们所说的可执行文件,从此CPU就可以直接执行灵活度高:C语言不仅提供多种运算符,还可以完成类似于计算机底层操作的位运算;语法简单,约束少;丰富多变的结构和数据类型;还拥有可以直接操作计算机硬件能力。可移植性高:可移植性高是指源代码在不需要做改动或只需稍加修改,就能够在其他机器上编译后正确运行,因此使用C语言的编译程序更便于移植。另外,无论是Windows系统、Linux系统还是苹果的MacOS系统,抛开现象看本质,它们都与C语言有不可分割的联系。我的第一个C语言程序打印HelloWorld#include<stdio.h>intmain(){printf("HelloWorld\n");return0;}代码说明/**我的第一个C语言程序**/#include<stdio.h>//引入模块/**main()主方法,一个c程序只有一个主方法**/intmain()//int表示返回值类型{printf("HelloWorld\n");//语句需要分号结尾return0;//返回值}运行测试在运行之前,Linux需要安装gcc,安装命令yuminstallgcc#编译C程序[root@nodesie1]#gcctest.c-otest#执行编译后的C程序[root@nodesie1]#./testHelloWorld
使用非直接缓冲区方式完整代码:点我查看创建文件流//创建文件流FileInputStreamfis=newFileInputStream("1.jpeg");//输入文件FileOutputStreamfos=newFileOutputStream("2.jpeg");//输出文件获取通道通过文件流获得通道//获取通道FileChannelfisChannel=fis.getChannel();FileChannelfosChannel=fos.getChannel();分配缓存区(Buffer)//分配指定大小的缓冲区ByteBufferbyteBuffer=ByteBuffer.allocate(1024);传递数据把输入通道里数据,通过Buffer传递给输出通道,最后输出//将通道的数据存入缓冲区while(fisChannel.read(byteBuffer)!=-1){byteBuffer.flip();//切换为读取数据模式//将缓冲区的数据写入通道中fosChannel.write(byteBuffer);byteBuffer.clear();}关闭通道&流//关闭通道&流fisChannel.close();fosChannel.close();fis.close();fos.close();使用内存映射文件方式完整代码:点我查看创建文件流//创建文件流FileInputStreamfis=newFileInputStream("1.mp4");//输入流FileOutputStreamfos=newFileOutputStream("2.mp4");//输出流获取通道通过文件流获得通道//获取通道FileChannelfisChannel=fis.getChannel();FileChannelfosChannel=fos.getChannel();创建内存映射文件//创建内存映射文件MappedByteBuffermappedByteBuffer=fisChannel.map(FileChannel.MapMode.READ_ONLY,0,fisChannel.size());fosChannel.write(mappedByteBuffer);关闭流fisChannel.close();fosChannel.close();
IO&NIO的区别NIOJavaNIO(NewIONonBlockingIO)是从Java1.4版本引入的一个新的IOAPI,可以替代标准的JavaIOAPI。NIO与原来的IO有着同样的作用和目的,但是使用的方式不同,NIO支持面向缓冲区的、基于通道的IO操作。NIO将更加高效的方式进行文件读写操作。区别IONIO面向流(Stream)面向缓冲区(Buffer)阻塞IO(BlockingIO)非阻塞IO(NonBlockingIO)无选择器(Selecors)缓冲区(Buffer)创建缓冲区(allocate)缓冲区本质就是数组,用于存储不同的数据缓冲区类型:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer//分配一个指定大小的Byte缓冲区(大小必须指定)ByteBufferbbuffer=ByteBuffer.allocate(1024);读&取缓冲区的两大核心方法缓冲区放入数据(put)放入方法put()有很多重载这里说明一下第一种:put(byte[]src,intoffset,intlength)byte[]src:放入缓冲区的数组intoffset:放入缓冲区数组的偏移量intlength:偏移量向后放入length位数据到缓冲区后两个参数为可选,不设置,表示把整个数组放入缓冲区。//Strings="abcde";byte[]bytes=newbyte[]{'a','b','c','e','f'};//数据放入缓冲区//bbuffer.put(s.getBytes());bbuffer.put(str.getBytes());读取缓冲区数据(get)在读取缓冲区数据之前,需要转换缓冲区模式为读取模式。bbuffer.flip();//读取缓冲区数据首先需要切换缓冲区模式System.out.println(bbuffer.get(0));//97打印出来的是字节码清空缓冲区但是'清空'后,缓冲区的数据依然存在,处于被遗忘状态bbuffer.clear();缓冲区的四大核心属性大小关系:mark<=position<=limit<=capacitycapacity:容量,代表缓冲区存储数据的大小,一旦声明不能改变limit:界限,表示缓冲区可以操作数据的大小(limit后的数据不能进行读写)position:位置,表示缓冲区正在操作数据的位置。mark:标记,表示记录当前position的位置,可以通过reset()恢复到mark位置。创建缓冲区时写入数据时读取数据时mark记录position的位置@Testpublicvoidtest(){byte[]bytes=newbyte[]{'a','b','c','e','f'};//创建放入数据切换模式ByteBufferbuf=ByteBuffer.allocate(10);buf.put(bytes);buf.flip();//记录position的位置buf.mark();//创建新的数组取出缓冲区的数据,添加到des字节数组byte[]des=newbyte[buf.limit()];buf.get(des,0,2);//输出数组的值System.out.println(newString(des,0,2));System.out.println(buf.position());buf.reset();//恢复position到mark位置System.out.println(buf.position());}
申请地图开发者Api申请网址:http://lbsyun.baidu.com/apiconsole/key#/home说明mui使用申请百度地图Api秘钥,App端和浏览器端秘钥都需要申请。申请App端秘钥需要填写的项应用名称:自己写应用类型:AndroidSDK发布版SHA1(HBuilder官方提供的测试码):BA:AD:09:3A:82:82:9F:B4:32:A7:B2:8C:B4:CC:F0:E9:F3:7D:AE:58包名:io.dcloud.HBuilder(在HBuilder发行打包里查看)申请浏览器端秘钥注意使用*号进行网址通配查看获取到的秘钥秘钥(Ak)使用浏览器端秘钥引入百度地图SDK<scripttype="text/javascript"src="http://api.map.baidu.com/api?ak=你的浏览器端ak&v=3.0">style与Html<styletype="text/css">html{height:100%}body{height:100%;margin:0px;padding:0px}#container{height:100%}</style><divid="container"></div><scripttype="text/javascript">varmap=newBMap.Map("container");varpoint=newBMap.Point(116.404,39.915);map.centerAndZoom(point,15);</script>Android秘钥填写打包测试浏览器、模拟器、真机都能运行