结果:
> show dbs
bj58 0.001GB
ganji 0.011GB
local 0.000GB
xiaozhu 0.000GB
> use ganji
switched to db ganji
> show collections
detailinfo
links
> db.links.find().count()
61988
> db.links.find()[0]
{
"_id" : ObjectId("574c361be002825f0b42c431"),
"url" : "http://biz.click.ganji.com/bizClick?url=pZwY0jCfsvFJshI6UhGGshPfUiqzIy78ph-6UMwd0v6ds1DOPWELnjmLPW-3sh6YURkknj7DEYw7EbEOPHcQEWD3wDmvPNuanNEYrHDkPWN3wbDYEdkknj70njTQsRkknjDznj0vPjn1gjTknHDYgjTknHDOPWELnjmLPW-0njTQn1ckgjTknHnQrRkknjDQn1-0njTQnHF0njTQnHEvPjmQnWnLrj0dP7kknjDQgjTknHD1nRkknj7BpywbpyOMgjTknH70njTQnHbkP1mkrjwxPRqkudkknjDkgjTknRkknjDkgjTknHEOsW9OsWcvsWckrRkknj7PULGGUAQ6s1N8naTCHy7WpyOYULPCrzKFUMw-UaKPmyn9Hdn9yaTQn7tQn7td2iKK0ZK_uRI-mbVGIatdn108n1m92DVcRDdnsaK_pyV-cDI-mvVf2iKjpZFfUyNfPHT8na3zPWmQsWDknBKHmyu60hbfPHnLsWnvxRkknjDdrHI0njTQsH70njTQsH70njTQEv6zUvd-gjTknyDdmhEzrj9YsHEOrjDVPAcQuBYOmvE1sHcQPHnQmhEkrynkPE&v=2"
}
> db.detailinfo.find().count()
60765
> db.detailinfo.find()[6198]
{
"_id" : ObjectId("574c8677e002826842500b08"),
"title" : "露得清水活盈透旅行装乳霜15克+保湿水20ML - 20元",
"url" : "http://bj.ganji.com/meironghuazhuang/1808653812x.htm",
"price" : 20,
"location" : [
" 北京",
"丰台",
"科技园区"
],
"date" : "03-05 09:23",
"condition" : null,
"goods_type" : "护肤品"
}
>
总结
- 如何判断是否是最后一页
两种实现思路
1,是否有分页符
if detail_soup.find("ul","pageLink"):
if detail_soup.select(".pageLink"):
如果没有,pass
2,是否元素数量足够
如果没有,同样 pass
-
li
元素
js-item
加载?
课上老师点了一下,没明白……
- 爬取的小技巧
应对网站对ip的请求限制,使用proxies
http://cn-proxy.com/
random.choice(list)
大网站承压性很好,可以不在请求间设置间隔,但对同一个ip的请求频次有限制
- 不抓商家帖子
因为永远存在,不知道什么时候交易成功的
- 判断是否已经交易成功
交易成功的物品已经下架,爬取时需要跳过
找一个不存在的页面 ,查看有无不同可以进行判断
- 断点续传
在详细页的信息集合中添加详情页的 url 字段
用爬取列表页获得的url数据 减 已经爬取的详情页中的 url ,取子集
db_urls = [item['url'] for item in info_links.find()]
item_urls = [item['url'] for item in info_detail.find()]
result_urls = set(db_urls) - set(item_urls)
- 抓取类目,乱码
url = "http://bj.ganji.com/wu/"
web_data = requests.get(url)
# print web_data.encoding # 查看编码类型
# ISO-8859-1
修改
web_data.encoding = "utf-8"
- 内容提取
有几处比较折腾
1,发布时间
页面呈现
05-30 发布
html
<i class="pr-5">
05-30 发布 </i>
列表化,可以看出字符的编码
if date_exist.get('class')[0] == "pr-5": # 不是都有发布时间
date = date_exist.get_text().strip().split(u'\xa0')[0] # 获取元素内容,去除两边空格,用中间的不间断空格分割为列表
2,成色
html
<li>
<label>新旧程度:</label>
全新,可送货(赠床垫 | 有床头柜 | 有床箱 | 有抽屉)
</li>
用.stripped_strings
获取所有下级元素的内容,形成列表
condition = list(detail_soup.select(".second-det-infor.clearfix li")[0].stripped_strings)
打印列表
[u'\u65b0\u65e7\u7a0b\u5ea6\uff1a', u'\u5168\u65b0\uff0c\u53ef\u9001\u8d27\uff08\u8d60\u5e8a\u57ab | \u6709\u5e8a\u5934\u67dc | \u6709\u5e8a\u7bb1 | \u6709\u62bd\u5c49\uff09']
获取列表的第二个元素
condition = list(detail_soup.select(".second-det-infor.clearfix li")[0].stripped_strings)[1]
得到
全新,可送货(赠床垫 | 有床头柜 | 有床箱 | 有抽屉)
用','
做分割符,获取第一个元素
condition = list(detail_soup.select(".second-det-infor.clearfix li")[0].stripped_strings)[1].split(','.decode("utf-8"))[0]
print condition
最终结果
全新
3,交易地点
<ul class="det-infor">
<li>
<label>类 型:</label>
<span>
<a href="/shafachaji/" target="_blank">沙发</a>
</span>
</li>
<li>
<label>价 格:</label>
<i class="f22 fc-orange f-type">3</i>元<i class="line">|</i>
<span class="f12 fc-gray">参考价格:100-699元</span>
<a target="_blank" gjalog="100000000013000100000010" href="http://www.ganji.com/hangqing/?mod=detail&to=devalue&url=shafachaji" class="p-z-type">近期价格走势<i class="p-z-icon"></i></a>
</li>
<li>
<label>交易地点:</label>
<a href="/jiaju/" target="_blank"> 北京 </a> - <a href="chaoyang/" target="_blank">朝阳</a>
</li>
尽量在爬取的时候将数据变得规整
如果用stripped_strings
,会将子元素的内容、子元素间的内容也弄进来
>>> location = list(detail_soup.select(".det-infor li")[2].stripped_strings)
>>> print location
>>> [u'\u4ea4\u6613\u5730\u70b9\uff1a', u'\u5317\u4eac', u'-', u'\u671d\u9633']
.det-infor li [0] # 类型
.det-infor li [1] # 价格
.det-infor li [2] # 交易地点
可以获取 ul 父元素下第三个 li 元素 下的所有 a 子元素
<li>
<label>交易地点:</label>
<a href="/jiaju/" target="_blank"> 北京</a> -
<a href="beijingzhoubian/" target="_blank">北京周边</a>
</li>
detail_soup.select('ul.det-infor > li:nth-of-type(3) > a')
ul.det-infor > li:nth-of-type(1) # 类型
ul.det-infor > li:nth-of-type(2) # 价格
ul.det-infor > li:nth-of-type(3) # 交易地点
用 map 和 lambda 函数,对列表中每一项获取元素内容,再用 list 制成列表
list(map(lambda x:x.text,detail_soup.select('ul.det-infor > li:nth-of-type(3) > a')))
#wrapper > div.content.clearfix > div.leftBox > div:nth-child(3) > div > ul > li:nth-child(3)
# print location[0], location[1]
地点可能不止一个元素,就使用列表存储
- 选择器
ul.det-infor > li:nth-of-type(3) > a
与 ul.det-infor > li:nth-child(3) >a
BeautifulSoup 不识别通过 google 开发者工具获取的CSS选择器nth-child
(第几个子元素而且是特定元素),可以用nth-of-type
(第几个特定元素)替换。
通过 google 开发者工具获取的CSS选择器:
#wrapper > div.content.clearfix > div.leftBox > div:nth-child(3) > div > ul > li:nth-child(3) > a:nth-child(2)
两者的区别:
the-difference-between-nth-child-and-nth-of-type
两次尝试:
>>> location = list(map(lambda x:x.text,detail_soup.select('ul.det-infor > li:nth-child(3) > a')))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Users/chao/Desktop/projects/web_crawler/venv-foo/lib/python2.7/site-packages/bs4/element.py", line 1481, in select
for candidate in _use_candidate_generator(tag):
File "/Users/chao/Desktop/projects/web_crawler/venv-foo/lib/python2.7/site-packages/bs4/element.py", line 1442, in recursive_select
for i in tag.select(next_token, recursive_candidate_generator):
File "/Users/chao/Desktop/projects/web_crawler/venv-foo/lib/python2.7/site-packages/bs4/element.py", line 1400, in select
'Only the following pseudo-classes are implemented: nth-of-type.')
NotImplementedError: Only the following pseudo-classes are implemented: nth-of-type.
>>> location = list(map(lambda x:x.text,detail_soup.select('ul.det-infor > li:nth-of-type(3) > a')))
>>> print location
[u' \u5317\u4eac', u'\u671d\u9633']
>>>
对于:nth-of-type(3)
,之前一直很疑惑,但没有去进一步了解,这次查了一下,原来这属于 CSS3 中定义的选择器,属性、id、div>p
这些是 CSS1、2 中定义的
- 可迭代的对象
可以通过 for 循环 或 list() 处理
- 数字应该转为 数值 再存入数据库
否则不能搜索,抓取下来也没有意义
- 导入模块
只导入函数,不导入 集合 ,可以?
导入,即使是 from ... import ... ,都会运行模块所有顶层代码
其他模块虽然没法直接调用,但函数内部可以调用
- 怎么将大量详情页的 url 传递给函数,并使用多进程?
我多虑了,几万数据的读取还是很轻松的
- 爬虫效率
如果是单核电脑,多进程不会有效,甚至降低效率。如果单核,多进程 效率低于 多线程。否则,进程效率高于线程
页面解析速度,lxml 库,速度是 beautiful 的 10 倍
import lxml
网络请求速度,异步非阻塞
- using-pymongo-with-multiprocessing
/Users/chao/Desktop/projects/web_crawler/venv-foo/lib/python2.7/site-packages/pymongo/topology.py:75: UserWarning: MongoClient opened before fork. Create MongoClient with connect=False, or create client after forking. See PyMongo's documentation for details: http://api.mongodb.org/python/current/faq.html#using-pymongo-with-multiprocessing>
"MongoClient opened before fork. Create MongoClient "
http://api.mongodb.com/python/current/api/pymongo/mongo_client.html#pymongo.mongo_client.MongoClient
- 奇特的页面链接
常见的的:
http://bj.ganji.com/jiaju/2043426190x.htm
但是,也会有以下两种……
http://m.zhuanzhuan.58.com/Mzhuanzhuan/zzWebInfo/zzganji/zzpc/detailPc.html?infoId=736841308648177667&cateId=107&fullCate=107&fullLocal=1&zzfrom=ganjiweb&zhuanzhuanSourceFrom=722
http://biz.click.ganji.com/bizClick?url=pZwY0jCfsvFJshI6UhGGshPfUiqBmyOMUvOMs1ckrjc3n1TYPjK3sh6YURkknjDkPHmOrND3Pj-jnjmYEY7jP1KjE1TzEHcYnbE1nH6APRkknj70njTQsRkknjDvP1NLP1T3nH60njTQnHw0njTQnWT3nW91njEYn7kknjDvn7kknjDvn7kknjDQPj60njTQnHF0njTQnHEvPjN3PjnLP1NOnRkknjDzn7kknjDQn170njTQmh-buA-8udkknjDQgjTknHDOnj0vnj9Yg16x0AI0njTQn7kknj70njTQn7kknjDQnHc8nWn8nB3Ln7kknj7PULGGUAQ6s1N8naTCHy7WpyOYULPCrzKFUMw-UaKPmyn9Hdn9yaTQn7tQn7td2iKK0ZK_uRI-mbVGIatdn108n1m92DVcRDdnsaK_pyV-cDI-mvVf2iKjpZFfUyNfPHT8na3zPWmQsWDknBKHmyu60hbfPHnLsWnvxRkknjDdrjw0njTQsH70njTQsH70njTQEv6zUvd-gjTknyNkrH9vrj-6sH01nWEVPjbYuBY3P1c3synzPW6-nyu6nyFhrT&v=2
页面爬取规则不同,导致出错
http://m.zhuanzhuan.58.com/Mzhuanzhuan/zzWebInfo/zzganji/zzpc/detailPc.html?infoId=736841308648177667&cateId=107&fullCate=107&fullLocal=1&zzfrom=ganjiweb&zhuanzhuanSourceFrom=722
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/pool.py", line 558, in get
raise self._value
IndexError: list index out of range
- 获取详情
有的页面,有时获取详情会失败,有时成功
加了个延时
- 不同页面的获取
分别单独尝试 每个类目的列表页、几种可能的详情页url、详情页不同的规则爬取方式的制定
- 从集合读取文档
读取文档的顺序是随机的
网友评论