├── A Taobao spider should take notice.md ├── Ajax.md ├── Readme.md ├── crawl wechat article.md ├── django ├── The Django template language For Python programmers.md ├── auth.md ├── csrf.md ├── logging.md ├── work with forms.md └── 性能和优化.md ├── http权威指南笔记.md ├── img ├── pl │ └── ruby │ │ └── ruby_doc.jpg └── wechat │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ └── 7.png ├── js ├── jquery_note.md ├── js design patten.md ├── js语法特性.md ├── js高级程序设计笔记.md └── vue-component.md └── programing language ├── haskell └── learn haskell_1.md └── ruby └── ruby_1.md /A Taobao spider should take notice.md: -------------------------------------------------------------------------------- 1 | ## 淘宝爬虫需要注意事项 2 | --- 3 | 最近在做一个关于淘宝同款的爬虫,即搜索与淘宝某个类似商品的同款商品,并记录下来这些同款商品的图片。商品由一个文件提供,里面是一些web的访问日志, 需要从里面提取出uniqueid,是一串数字,然后再和淘宝搜同款的url进行拼接,生成url进行访问,然后解析页面内容,提取数据,记录。 4 | 5 | 看似挺简单的工作,竟然也是踩坑不少,记录下来,下次抓取的时候注意。 6 | 7 | 1. **速率** 8 | 众所周知,淘宝在反爬虫方面是出了名的严厉。一开始我的速率是3s请求一次,在少量样本的情况下(不到100个)工作良好,给我造成了错觉,以为这个速率是可以的。后来在样本变大的时候,就被无情地封杀了。换台服务器,接着来,4s,然后大约10分钟又是被封杀。由于服务器资源有限,只好速率降成10s一次,工作良好(还好这项任务对速率没有要求)。 9 | 2. **http header** 10 | 爬虫自然要伪装成正常浏览器了,所以一开始我几乎把所有请求头都加上了(包括登陆过后的cookie)。但是过了一会就发现请求的页面被跳转了,需要登陆。个人猜测应该是服务器那边定时换cookie,导致旧cookie失效。 11 | 山穷水复,柳暗花明。当我一筹莫展之际,我把所有的请求头都去掉,尝试一下,结果竟然成功抓取了! 12 | 3. **页面解析、下一页** 13 | 同款页面返回的数据并没有在html标签里,而是在js里,然后在浏览器端经过计算,填充到应该在的标签里。在`python`如果不用`phantomjs`的话,就无法解析html结构进行抓取了。好在数据是json格式的,从返回的html文本中用正则表达式提取出json,然后再loads为python的数据结构,取出想要的元素(我这里是pic_url) 14 | 经过观察,同款的下一页是直接在url后面加个`&s=60`这样的参数代表第二页从第60个商品开始。第三页自然就是`&s=120`了。但是如果想要得到页数,还要在上面返回的json中进行查找。我想到了一个比较简单的方法,利用`python`的`itertools.count`生成器,生成从0开始的整数,然后乘以60,进行url拼接。假如到某一页停止了,无法解析出`pic_url`字段,会产生异常,然后就从这里跳出本次循环,进行下一个uniqid的遍历 15 | 4. **uniqueid的去重,暂存** 16 | 这是我第一次没有考虑到的。我没有仔细看提供样本的文件,只知道有很多数字。后来第二遍看的时候,发现里面有好多重复的,应首先去重。当然去重之后还是有好多数据。我就想,如果爬虫异常,重新开始,还是需要从头开始进行抓取,本来爬虫就不快,这要等到何年何月?好在`python`有针对其内置类型的序列化模块,可以将其保存在文件里。对于`uidlist`,我将其序列化到文件上,然后爬虫开始的时候再反序列化这个文件,取出`uidlist`,对于其中的元素,抓取成功后将其从`uidlist`中删除,再将`uidlist`序列化为文件。这样可以保证下次开始的时候之前的都已抓取过,无需重新抓取。 17 | 18 | 抓取代码如下: 19 | ```python 20 | #coding:utf8 21 | import time 22 | import re 23 | import json 24 | import itertools 25 | import sys 26 | import requests 27 | from itertools import count 28 | import traceback 29 | import cPickle 30 | 31 | 32 | def json_handler(uid,json_dict): 33 | if not json_dict: 34 | return False 35 | try: 36 | item_list = json_dict['mods']['recitem']['data']['items'] 37 | pic_set = set() 38 | with open('new_result.log','a') as f: 39 | f.write(uid[1:]+'\n') 40 | for item in item_list: 41 | pic_set.add(item['pic_url']) 42 | for pic in pic_set: 43 | f.write(pic+'\n') 44 | return True 45 | except KeyError: 46 | print 'None' 47 | return False 48 | 49 | 50 | def parse_url(u): 51 | try: 52 | res = requests.get(u).text 53 | json_str = re.findall(' = {.*};', res)[0][3:-1] 54 | json_dict = json.loads(json_str) 55 | return json_dict 56 | except: 57 | return None 58 | 59 | #读取pickle文件 60 | with open(r'pickle_file') as pf: 61 | uidlist = cPickle.load(pf) 62 | 63 | while uidlist: 64 | try: 65 | uid = uidlist[0] 66 | url ="http://s.taobao.com/search?type=samestyle&app=i2i&rec_type=&uniqpid=%s&s=0"%uid 67 | num = count(0) 68 | while True: 69 | qstring = 60*num.next() 70 | new_url = url.replace('&s=0','&s='+str(qstring)) 71 | print new_url 72 | try: 73 | jd = parse_url(new_url) 74 | except: 75 | with open('except.log','a') as f: 76 | f.write(traceback.format_exc()+'\n'+new_url) 77 | r = requests.get(new_url).text 78 | with open('html.html','w') as w: 79 | w.write(r.encode('utf8')) 80 | break 81 | if not json_handler(uid,jd): 82 | break 83 | #将 此uid从uidlist删除,并保存pickle文件 84 | uidlist.pop(0) 85 | with open('pickle_file','w') as pf: 86 | cPickle.dump(uidlist,pf) 87 | time.sleep(10) 88 | except: 89 | pass 90 | 91 | ##another file 92 | with open(r'uniqpid.log') as f: 93 | content = f.read() 94 | uidlist = re.findall('-\d+', content) 95 | #去重 96 | uidlist = list(set(uidlist)) 97 | 98 | with open(r'pickle_file','w') as pf: 99 | cPickle.dump(uidlist,pf) 100 | ``` -------------------------------------------------------------------------------- /Ajax.md: -------------------------------------------------------------------------------- 1 | #### Ajax 2 | --- 3 | 4 | + create xhr object: 5 | 6 | ```js 7 | var request; 8 | if(window.XMLHttpRequest){ 9 | request = new XMLHttpRequest(); 10 | } 11 | else{ 12 | xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 13 | } 14 | ``` 15 | 16 | + open(method,url,async),true为异步,默认为true。 17 | + send(string),string为请求体,post的请求体不能为空(为空意义不大)。get请求体为空,string可以不填。 18 | ```js 19 | request.open('post',http://baidu.com,true); 20 | request.setRequestHeader("Content-type","application/x-www-form-urlencoded");//表单 21 | request.send("name='fuck'&password='123'"); 22 | ``` 23 | + 获得响应:`response.responseText`,`responseXML`,`status`,`statusText`,`getResponseHeader()`,`getAllResponseHeader()` 24 | + readyState,代表服务器响应的状态:0 未初始化,opne未调用;1,服务器连接已经建立,open已经调用;2,请求已接受,接收响应头信息 3接收响应主体 4响应已就绪,响应完成了 25 | + readyStatechange事件: 26 | ```js 27 | request.onreadystatechange = function(){ 28 | if(request.readyState === 4 && request.status===200){ 29 | doSth(); 30 | } 31 | } 32 | ``` 33 | + 通过fiddler的conposer模块可以模拟表单提交等动作 34 | + post提交时open和send函数中间的`setRequestHeader`不可少 35 | 36 | + json 长度短,速度快,可以直接转化为js对象,已经替代了html 37 | + json在js中解析 eval 和 38 | ```js 39 | var jsondata = '{"name":"fuck"}'; 40 | var jsonobj = eval(jsondata); 41 | var jsonobj2 = JSON.parse(jsondata); 42 | ``` 43 | + eval中若字符串中有alert(1)这样的,会直接执行;建议用JSON.parse 44 | + 服务端设置`Content-type:application/json 45 | + jquery 中的ajax: 46 | ```js 47 | jQuery.ajax({type:"GET", 48 | url:"service.php?name=100", 49 | //data:{}; 若方法为POST,此参数为POST数据 50 | dataType:"json",//预期返回的MIME类型 51 | success:function(data){ 52 | //成功时调用的函数 status==200时 53 | }, 54 | error:function(jqXHR){ 55 | //失败时调用的函数 56 | } 57 | }); 58 | ``` 59 | + 同源策略,跨域: 协议,子域名,主域名,端口之中有一个不同时,则为不同域。localhost 和 127.0.0.1也是不同域。js不允许跨域操作/调用其他域的对象。返回status为0不是200 60 | + 处理跨域 61 | 1. 代理,与js相同域的服务器端代理请求,之后再返回请求的结果 62 | 2. JSONP 处理主流浏览器**GET**请求跨域数据访问问题。利用了`