基于这个博主代码的进一步开发https://blog.csdn.net/chenwh_cn/article/details/97647127
三个参数
第一个是要搜索的关键字
第二个是排序后返回前多少项(默认=10)
第三个是遍历的页数(默认=20)(去除广告,一页有10~20条返回结果)
如果是-1就是遍历所有结果,如果你搜的词比较大众,返回结果很多那就停不下来了,目前没有写提前中止的代码,如果写的话应该是用kbhit
python csdn.py 扫雷 20 -1
或者也可以手动
import csdn
c=csdn.Csdn('扫雷',20)
c.run(-1)
注意点
line18改成自己chromedriver.exe的路径,没安装的自己百度
line57&58改成自己的github账号密码
2019/9/29更新:
修正csdn登录框的xpath,由于国庆节小改版导致=
ω
\omega
ω=
修正github登录键的xpath,原因未知
增加了保存到csv文件的函数,采用pandas库的dataframe.to_csv()
如果没有这个库可以自己装一下,本来是用在深度学习的,我这里图方便借用一下
import sys
import time
import re#regular expression
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys
class Csdn():
def __init__(self, searchStr, displayCount):
self.searchStr = searchStr
self.displayCount = displayCount
self.retList = []
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('log-level=3')
self.driver = webdriver.Chrome(executable_path=r'C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe', options=options)
def sortResult(self):
print('sorting')
def sortKey(item):
#匹配字符串中的数字
counts = re.findall(r"\d+", item[0])
return int(counts[0])
#从大到小
self.retList.sort(key=sortKey, reverse=True)
def displayResult(self):
if self.displayCount > len(self.retList):
self.displayCount = len(self.retList)
for retItem in self.retList[:self.displayCount]:
print('{0:>10} {1} {2}'.format(retItem[0],retItem[1],retItem[2]))
def loginByGithub(self,driver):
'''
登录账号,否则不能看后几页的内容
我采用的是github登录
用qq会比较麻烦
'''
print('使用github登录中')
print('点击主页登录按钮')
#driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/ul/li[4]/a[1]').click()
driver.find_element_by_xpath('//*[@id="csdn-toolbar"]/div/div/ul/li[3]/a').click()
#弹出一个新界面,切换过去
handles = driver.window_handles
csdn0=handles[0]
csdnLogin=handles[1]
driver.switch_to.window(csdnLogin)
print(driver.current_url)
#点击社交账号登录
driver.find_element_by_xpath('//*[@id="app"]/div/div/div[1]/div[2]/div[5]/ul/li[4]').click()
#点击github图标,验证少,容易实现
driver.find_element_by_xpath('//*[@id="app"]/div/div/div[1]/div[2]/div[5]/ul/li[5]/a[5]').click()
#弹出github界面,切换过去,如果之前登录过github则是直接成功,需要考虑
driver.switch_to.window(driver.window_handles[2])
print(driver.current_url)
driver.find_element_by_id('login_field').send_keys('youraccount')
driver.find_element_by_id('password').send_keys('yourpassword')
#driver.find_element_by_xpath('//*[@id="login"]/form/div[2]/input[7]').click()
driver.find_element_by_xpath('//*[@id="login"]/form/div[2]/input[8]').click()
#把多余的界面关掉
driver.switch_to.window(csdn0)
driver.close()
driver.switch_to.window(csdnLogin)
driver.close()
#切换回登录过的csdn主界面
driver.switch_to.window(driver.window_handles[0])
def run(self,maxPage=20):
driver = self.driver
driver.get('https://blog.csdn.net/')
print(driver.current_url)
self.loginByGithub(driver)
driver.find_element_by_id('toolber-keyword').send_keys(self.searchStr)
driver.find_element_by_id('toolber-keyword').send_keys(Keys.RETURN)
handles = driver.window_handles
driver.close()
driver.switch_to.window(handles[1])
#结果数
numStr=driver.find_element_by_class_name('page-nav').find_element_by_class_name('text').text
print(numStr)
print('返回搜索1~{}页的整合结果'.format(maxPage))
i=0
zeroNum=0
while(True):
i+=1
if not((i<=maxPage)or(maxPage==-1)):break
url='https://so.csdn.net/so/search/s.do?p={pageNum}&q={searchStr}&t=blog&domain=&o=&s=&u=&l=&f='.format(pageNum=i,searchStr=self.searchStr)
driver.get(url)
#获取一页搜索结果的list
sList=driver.find_elements_by_xpath("//dl[@class='search-list J_search']")
if len(sList)==0:break#遍历完所有结果了
ret=0
for s in sList:
try:
strs=s.find_element_by_class_name('author-time').text.split()
title=s.find_element_by_xpath('dt/div/a[1]').text
if self.searchStr in title:
ret+=1
self.retList.append((strs[4],strs[3],title))
except BaseException:
#没有阅读数显示时会被捕获
continue
print('page:{},ret:{}'.format(i,ret))
#如果遇到一大串0就提前中止
if ret==0:
zeroNum+=1
if zeroNum>5:break
else:
zeroNum=0
self.sortResult()
self.displayResult()
self.saveToCsv()
driver.close()
driver.quit()
def saveToCsv(self):
import pandas as pd
df=pd.DataFrame(self.retList)
if self.displayCount > df.shape[0]:
saveCount=df.shape[0]
else:
saveCount=self.displayCount
timeStr=time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime())
csv_name='{}@{}.csv'.format(self.searchStr,timeStr)
df[:saveCount].to_csv(csv_name,sep=',',header=False,index=True,encoding='gbk')
print('save to csv successfully')
if __name__ == '__main__':
argc=len(sys.argv)
if argc==1:
searchStr="验证码识别"
displayCount=20
pageMax=5
else:
searchStr=sys.argv[1]
if argc==2:
displayCount=10
pageMax=20
elif argc==3:
displayCount=int(sys.argv[2])
pageMax=20
else:
displayCount=int(sys.argv[2])
pageMax=int(sys.argv[3])
csdn = Csdn(searchStr, displayCount)
csdn.run(pageMax)
几个关键点
1.如果不登录只能看搜索结果的第一页
目前使用github登录,因为没有验证码。一开始想用qq的,为了绕开滑块验证码,使用的方法是先在电脑上手动登录qq然后网页快捷登录。但是不知道为什么find_element找不到元素,明明确实在浏览器中可以检索到。
2.
虽然csdn下面的导航栏只有1~20但是是可以通过改url跳到后面几页的,体现在line89这行代码,pageNum是几就是第几页,也不用find_element在点击导航按钮,更快捷
url=‘https://so.csdn.net/so/search/s.do?p={pageNum}&q={searchStr}&t=blog&domain=&o=&s=&u=&l=&f=’.format(pageNum=i,searchStr=self.searchStr)
3.csdn按相关度排序
初版的时候,我查看了一下返回的结果,很糟糕,有很多都和关键字毫不相关= =。我手动更改url跳到后面几页,发现150页之后都没什么大关系了,有的文章,只是中间提到了一次关键字,但主题不是这个,具体分布可以看最后的图。如果说这些阅读数很高,那就会把前面相关度高但是阅读数低的顶掉了。解决方法没有采用页数限制,因为不同关键字的这个分水岭页数不一样;采用了标题栏中有关键字的文章才放入。具体见line99
4
需要改进的地方主要是效率,搜索结果随随便便就几百页。需要多线程开多个浏览器同时爬虫。顺带一提,一个浏览器应该不能对多个窗口开多个线程操作,因为find_element只能找当前窗口的元素,会互相干扰
附
运行结果
关键字“验证码识别”
相关度调查
虽然没有直接对每篇文章具体含有多少关键字进行分析,但是通过对每页所有的返回结果分析,标题中含关键字的比例确实随着页数增多而逐渐下降。