最近接到一个体力活儿,需要获取几百家企业的所属行业信息。某眼查需要登录,某查查免登录可用。由于水平有限,也为了节省时间,选用某查查。除此之外,脚本编写时暴露了许多的问题,需要后续深入研究,也都记录在本文中。
update:2019-02-21 10:47:50
技术要点:页面分析、多进程/多线程、文件读写同步、验证码问题、状态码问题、代理切换、动态加载数据获取、结果整合
页面解析
请求url:https://www.qichacha.com/search?key=公司全称
打开链接后显示几个候选公司,第一个候选项匹配度最高。获取第一个候选项位置,和输入的公司名作比较,如果完全匹配,则继续。否则,视为查询不到结果。完全匹配后,需要点击公司名,进行一次跳转。再在页面中找到行业信息的位置,获取其内容。
以上位置,可在chrome浏览器中用F12导出对应的xpath。
部分代码如下:
#-*-coding:utf-8-*-
import sys
reload(sys)
sys.setdefaultencoding('utf8')
import time
import os
import random
from selenium import webdriver
from multiprocessing import Process
def get_Cominfo(id,fullname):
id = str(id)
url2 = 'https://www.qichacha.com/search?key='+fullname
browser = webdriver.Firefox()
browser.get(url2)
t1 = browser.find_elements_by_xpath('//*[@id="searchlist"]/table/tbody/tr/td[2]/a')
ufname = unicode(fullname,"utf-8")
if ufname == t1[0].text:
h = t1[0].get_attribute('href')
browser.get(h)
t2 = browser.find_elements_by_xpath('//*[@id="Cominfo"]/table[2]/tbody/tr[5]/td[4]')
res = t2[0].text
browser.quit()
操作过程中发现,如果在第一个页面直接使用t1.click()
,会再打开一个标签页,但browser
对象仍然为第一个页面的。于是提取第一个页面中t1
对应的url,也就是第二个页面中的url,用browser.get(h)
直接从当前页面打开。最终res = t2[0].text
就是找到的行业信息字符串。
并发
经测试发现,单条查询耗时20s~180s,都是卡在一些网页广告的加载上面了,比如p0.baidu.com、ali.cdn之类的。眼睁睁地看到了数据,但网页左上角的图标一直在转圈,程序也停在那里。也许可以不等它加载完就动态刷新抓取,但不知道怎么做……只尝试使用多进程处理。
先把待查公司名存入一个txt文件中,每行一个。用python按行读取到一个列表中,遍历这个列表进行查询。多进程采用的multiprocessing
模块,对每项开启一个进程,用time.sleep(random.randint(14,28))
控制冷却时间,避免机器卡死。
if __name__ == '__main__':
print "[*] running..."
comlist = get_Comlist('company.txt')
start = time.time()
for i in range(len(comlist)):
if os.path.exists('result/'+str(i)+'.txt'):
print "%s already exists, jump it" %str(i)
continue
p = Process(target=get_Cominfo,args=(i,comlist[i].strip(),))
print 'Process %s running...' %str(i)
p.start()
freeze = random.randint(14,28)
print 'Next will start after %s seconds' %str(freeze)
time.sleep(freeze)
p.join()
print "Process end."
time.sleep(3000)
end = time.time()
print "[*] over! Cost seconds:",
print end-start
保存结果时,开始采用的存入一个txt中,出现读写占用
问题,报错一堆。如果要从python进程控制角度,又是资源锁、又是调度队列之类的,要在短时间内解决不太现实。不得不承认,这些东西没学扎实是真的,有空确实该巩固一些计算机基础了。说归说,业务问题还是要立刻马上解决的。如果一个文件不能解决问题,那就两个!实际上,对每一个结果都新建了一个txt,以行号id命名。最后汇总时,整理一下格式就好。
s = file('result/'+id+'.txt','a')
s.write(id)
s.write('\t')
s.write(fullname.strip())
s.write('\t')
s.write(res)
s.write('\n')
s.close()
以上是保存每一条结果的代码,由于不知道process
进程执行结果返回值在哪,便直接写到了get_Cominfo
函数内部。有机会的话,把python的各种进程、线程同步问题扫一遍。
对抗反爬虫
程序运行一段时间开始报405错误,猜测是请求频率太高被封。反爬策略,有可能是根据浏览器UA判断,换个浏览器,还是405,看样子是封了ip。
image.png换了个网,增大sleep时间,再次运行,一段时间后再次报错,提示输入验证码:滑动验证码+图片验证码。现在人工智能技术也有些进步,如果时间充足的话,selenium+OCR可以搞定。但是领导没有给那么多时间。多次检测发现,每个ip最多可以查50次,那么最简单粗暴的方法就是换ip!
kali自带proxychains,改下配置文件sudo vi /etc/proxychains.conf
,网上找些个代理用的ip,却发现各种运行报错。有时间再单独研究下。
最后,手动配置linux的代理模式。
代理ip地址,挨个试咯。一个IP质量分两个方面,一个是本机到该ip的延迟,一个是该ip到目标网站的延迟情况。希望有朝一日能写个ip质量筛选脚本。其实github上这类挺多的,就是看着头昏眼花,先不管了。由此引申,根据延迟速度评估,或许能写个代理ip反查工具。
比较逗的一个,刚连上ip,某查查立马提示输入验证码,看来用这种方法抓取某查查网站的不止我一个。更神奇的是,下意识地输入验证码,再运行第二个进程时,居然不需要验证了。
结果汇总
最终获取到如下文件:
管道命令:cat *.txt>../multi.txt
,python执行简单的shell命令可以用形如import os;os.system('ls')
实现。
排序问题,EXCEL中新建一个名为hangye的sheet,粘贴过去,手动排序。其实接下来的操作,没必要排序了。
真实的待填充表格的行业信息是断断续续的,比如1~3行是空的,第4行又有了,5~9行又是待填数据。获取行业信息时,使用了电子表格的筛选功能,直接粘贴进去的话,会覆盖掉隐藏的行的数据,似乎只能手动一条一条粘贴了,400条数据,这要粘到猴年马月啊!一点都不酷!再写个脚本?于是有了下篇:Python操作EXCEL电子表格
参考资料
- 《Python爬虫开发与项目实战》
- 《Python编程快速上手-让繁琐工作自动化》
写在最后
代码相当蹩脚,磕磕绊绊只比同事提前四五个小时完成,但成功地把重复性的任务转完成了创造性的工作。而且,学到了不少东西,还发现了自身的许多不足之处。总好过复制粘贴搜索刷到吐。
网友评论