1. 前文
皆さんこんにちは、シャオロンです。今日は Java 関連のテクノロジについては話しませんが、大学のコンテストでプロジェクトに使用したクローラーを共有したいと思います。
事情是这样的:
コンペプロジェクトには電子商取引に関するモジュールがありますが、データがなく、JD.comと淘宝網のPCデータをクロールする予定ですが、一部の写真は携帯電話のサイズを満たしていません。総合的に検討した結果、決定しました。 JD.com の携帯電話 Web ページ データをクロールするため
関連リンク:
私のプロジェクト「人工知能に基づくスマートキャンパスアシスタント v1.0.1」の一部の機能
https://www.bilibili.com/video/BV1XT4y1w7Xc
「京東モール」
https://so.m.jd.com/webportal/channel/m_category?searchFrom=home
まずはデータの最後の部分のスクリーンショットを見てみましょう、そうです、クロールされたデータはすべて json データとして保存されており、どこでも利用するのに便利です。
最終的に、数十万のデータが取得され、それらすべてがインポートされて、ElasticSearch
強力な検索システムとレコメンデーション センターが構築されました。
この期間中、私は多くの問題に遭遇し、多くの新しい知識を学びました。Javaをメインにやっていない人はJavaにしか関係ないと思います语言只是工具,是为了方便我们自身,所以具体应用场景使用什么方便就怎么来吧
。もちろんJavaでもクロールは可能ですが、個人的にはPythonほど強力で便利ではないと感じています。
この記事は、すべてのアイデアの生成の最初から最後まで、遭遇したさまざまな問題、そして問題の解決方法と私自身の意見と結論のいくつかを、読者に新しいアイデアをもたらすことを願って、プロセス全体を通して私の考えと考えを記録します。理解と認識 (テクノロジーの観点でも、問題解決のアイデアの観点でも)、もう 1 つの文: Xiaolong Python はあまり良くありません。欠陥がある場合は、メッセージを残して修正してください。また、学習して議論することもできます。技術交流を通じて共に進歩していきます。
あまりナンセンスではありません。Xiaolong の考え方に従い、段階的に見てみましょう。!
2. データベース設計
カテゴリ情報テーブル(product_category)
CREATE TABLE product_category(
category_id SMALLINT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '分类ID',
category_name VARCHAR(10) NOT NULL COMMENT '分类名称',
img VARCHAR(100) NOT NULL COMMENT '分类图片logo',
parent_id SMALLINT UNSIGNED NOT NULL DEFAULT 0 COMMENT '父分类ID--若为0则该层为父类',
category_level TINYINT NOT NULL DEFAULT 1 COMMENT '分类层级--该层为该分类第几层',
category_status TINYINT NOT NULL DEFAULT 1 COMMENT '分类状态--是否还可继续往下分,是1否0',
PRIMARY KEY pk_categoryid(category_id)
)ENGINE=innodb COMMENT '商品分类表'
商品情報テーブル(product_info)
CREATE TABLE product_info(
sku_id INT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '商品ID',
product_name VARCHAR(20) NOT NULL COMMENT '商品名称',
category_id1 SMALLINT UNSIGNED NOT NULL COMMENT '一级分类ID',
category_id2 SMALLINT UNSIGNED NOT NULL COMMENT '二级分类ID',
category_id3 SMALLINT UNSIGNED NOT NULL COMMENT '三级分类ID',
price FLOAT NOT NULL COMMENT '商品销售价格',
publish_status TINYINT NOT NULL DEFAULT 0 COMMENT '上下架状态:0下架1上架',
descript VARCHAR(100) NOT NULL COMMENT '商品描述',
spec_param VARCHAR(10000) NOT NULL COMMENT '商品规格参数',
title VARCHAR(100) NOT NULL COMMENT '商品标题',
PRIMARY KEY pk_productid(product_id)
) ENGINE = innodb COMMENT '商品信息表';
製品画像テーブル (productpicinfo)
CREATE TABLE product_pic_info(
product_pic_id INT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT '商品图片ID',
product_id INT UNSIGNED NOT NULL COMMENT '商品ID',
pic_desc VARCHAR(50) COMMENT '图片描述',
pic_url VARCHAR(200) NOT NULL COMMENT '图片URL',
is_master TINYINT NOT NULL DEFAULT 0 COMMENT '是否主图:0.非主图1.主图',
pic_status TINYINT NOT NULL DEFAULT 1 COMMENT '图片是否有效:0无效 1有效',
PRIMARY KEY pk_picid(product_pic_id)
)ENGINE=innodb COMMENT '商品图片信息表';
3. 分析
3.1. ページ分析
各カテゴリは複数の2段階および3段階の分類に分かれています
3.2. ページ要素の確認
解決する:
1. 実装アイデア
最初のレイヤーは category1 (モバイルデジタル、家電製品など) を取得し、2 番目のレイヤーは categorygeoy2 (人気のあるカテゴリ、モバイル通信など) を取得し、3 番目のレイヤーは categorygeoy3 (Xiaomi、Huawei、Honor...) を取得します。
2. データリクエスト
この Web ページでは、category1 が選択されるたびに ajax を使用してデータをリクエストし、基になるデータが 1 回リロードされるため、selenium模拟浏览器
自動クロールが選択されます。
# 总枢纽,控制每一级分类,详情爬取
def spider(driver):
# 得到ul列表
# 显示等待
wait(driver, '//*[@id="category2"]//li')
html = etree.HTML(driver.page_source)
lis = html.xpath('//*[@id="category2"]//li')
# 解析每个li标签
# or
for i, li in enumerate(lis):
# 分类太多,爬取时间太长,选取有用类爬取
if ( i == 1 or i == 3 or i == 8 or i == 26):
# 保存一级分类
category_id1 = li.xpath('./@id')[0]
category_name1 = li.xpath('.//text()')[0]
category_level1 = 1
category_status1 = 1
parent_id1 = 0
# path = 'category%s' %str(i+6)
path = '//*[@id="category%s"]/a' % str(i+6)
# driver.find_element_by_xpath('//*[@id="category18"]/a').click()
# time.sleep(2)
# driver.find_element_by_xpath('//*[@id="category27"]/a').click()
# time.sleep(2)
item = driver.find_element_by_xpath(path)
item.click()
time.sleep(3)
phone_html = etree.HTML(driver.page_source)
branchList = phone_html.xpath('//*[@id="branchList"]/div')
for j, item in enumerate(branchList):
# 一个item就是一个div
# 获取二级分类
print(j)
if(i == 14 and j == 0):
continue
category_name2 = (item.xpath('./h4/text()'))[0]
category_id2 = 'SecCategory'+str(category_id1).split('y')[1]
category_level2 = 2
category_status2 = 1
parent_id2 = category_id1
lis = item.xpath('./ul/li')
for k, li in enumerate(lis):
#这是由于数据太多,我控制category3只要6个就够了
if(k>5):
break
category_id3 = (li.xpath('./a/@id'))[0]
img = li.xpath('./a/img/@src')[0]
href = li.xpath('./a/@href')[0]
# 解析详情页
# ****detail(page_source)****
category_name3 = (li.xpath('.//span/text()'))[0]
category_level3 = 3
category_status3 = 0
parent_id3 = category_id2
print("进入第%s页爬取......" % (k+1))
detail_href = li.xpath('./a/@href')[0]
# 打开商品详情页,另开窗口
driver.execute_script("window.open ('%s')" % detail_href)
driver.switch_to.window(driver.window_handles[1])
time.sleep(2)
flag = isElementExist(driver, '//*[@id="pcprompt-viewpc"]')
if flag:
start = driver.find_element_by_xpath('//*[@id="pcprompt-viewpc"]')
start.click()
print('开始爬取第三级分类商品.....')
# driver.execute_script("arguments[0].click();", li)
# start = time.clock()
# 爬取详情信息
getList(driver, category_id1, category_id2, category_id3)
# end = time.clock()
print("爬取第三级分类商品完成.....")
driver.close()
driver.switch_to.window(driver.window_handles[0])
# 解析详情页
# time.sleep(2)
# 三级
category3 = {
"category_id": category_id3,
"category_name": category_name3,
"category_level": category_level3,
'category_status': category_status3,
"parent_id": parent_id3,
'img': img,
'href': href
}
list.append(category3)
# 二级
category2 = {
"category_id": category_id2,
"category_name": category_name2,
"category_level": category_level2,
'category_status': category_status2,
"parent_id": parent_id2
}
list.append(category2)
# 一级
category1 = {
"category_id": category_id1,
"category_name": category_name1,
"category_level": category_level1,
'category_status': category_status1,
"parent_id": parent_id1
}
list.append(category1)
with open('categoryList.json', 'w', encoding='utf-8') as fp:
json.dump(list, fp, ensure_ascii=False)
3. ポップアップウィンドウの問題
Web サイトにアクセスし始めてから、ポップアップ ウィンドウ (下の図に似たもの) がページを覆うようにジャンプし、ページ要素を取得してクリックするためにそれを使用できなくなり、場合によっては取得できなくなりましたselenium
。わかる、落ち込んでいたので判断する機能を追加しました価値があるか
# 判断某元素是否存在
def isElementExist(driver, path):
flag = True
try:
driver.find_element_by_xpath(path)
return flag
except:
flag = False
return flag
4. カテゴリリストを取得する
分析ページ。必要な情報を見つけて、
category[
category3 = {
“category_id”: category_id3,
“category_name”: category_name3,
“category_level”: category_level3,
‘category_status’: category_status3,
“parent_id”: parent_id3,
‘img’: img,
‘href’: href
}
]
分析の結果、3 段階に分類された製品がa标签超链接
製品リストに入るエントリであり、href を通じて Goods_list に入ることがわかります。
for k, li in enumerate(lis):
if(k>5):
break
category_id3 = (li.xpath('./a/@id'))[0]
img = li.xpath('./a/img/@src')[0]
href = li.xpath('./a/@href')[0]
# 解析详情页
# ****detail(page_source)****
category_name3 = (li.xpath('.//span/text()'))[0]
category_level3 = 3
category_status3 = 0
parent_id3 = category_id2
print("进入第%s页爬取......" % (k+1))
detail_href = li.xpath('./a/@href')[0]
driver.execute_script("window.open ('%s')" % detail_href)
driver.switch_to.window(driver.window_handles[1])
time.sleep(2)
flag = isElementExist(driver, '//*[@id="pcprompt-viewpc"]')
if flag:
start = driver.find_element_by_xpath('//*[@id="pcprompt-viewpc"]')
start.click()
print('开始爬取第三级分类商品.....')
# driver.execute_script("arguments[0].click();", li)
# start = time.clock()
getList(driver, category_id1, category_id2, category_id3)
# end = time.clock()
print("爬取第三级分类商品完成.....")
5. カテゴリ 4 リストを入力します
ページを分析して、必要な関連情報を見つけてください
{
skuid
price
title
url
img 图片到详情页与海报图一起获取
}
分析の結果、ハイパーリンク –tourl を使用すると PC 側のページにリダイレクトされることが判明しました。
そこで、各商品のアドレスを解析した結果、
1)https://item.m.jd.com/product/100004559325.html?sku=100004559325&price=1599.00&fs=1&sid=&sf=newM&sceneval=2&pos=1&csid=5e01c492804b348d18864f64a9d55e52_1583 067332315_1_1583067332316&ss_symbol=10&ss_mtest=m-list-none,~&key=
2)https://item.m.jd.com/product/100008348542.html?sku=100008348542&price=5999.00&fs=1&sid=&sf=newM&sceneval=2&pos=2&csid=5e01c492804b348d18864f64a9d55e52_1583 067332315_1_1583067332316&ss_symbol=10&ss_mtest=m-list-none,~&key=
。。。。。。。
各リンクには価格とは異なる彼の skuid のみが含まれていることがわかりました。試してみることはできますが、時々他の小さな変更があり、面倒に感じます。そこで、js とリンクを調べた後、
ユニバーサルアドレスhttps://item.m.jd.com/product/(skuid).html?m2wq_ismiao=1
。その後、彼の skuid さえ取得できれば、スムーズに詳細ページにアクセスできます。
SKU パーツ データ コードを収集します。
print('开始爬取商品详情页.....')
loadPage(driver, 1)
html = etree.HTML(driver.page_source)
wait(driver, "//*[@id='itemList']/div[@class='search_prolist_item']//div[@class='search_prolist_price']//span")
divs = html.xpath("//*[@id='itemList']/div[@class='search_prolist_item']")
skus = []
for i, div in enumerate(divs):
try:
skuid = div.xpath('./@skuid')[0]
except:
skuid = 0
try:
title = div.xpath(".//div[@class='search_prolist_title']/text()")[0].strip()
except:
title = ""
try:
price = div.xpath(".//div[@class='search_prolist_price']//span/text()")[0].strip()
except:
price = 0
# 这是当时写了个判断,但是出错,又嫌代码缩进不好处理,就改了个1
if (1):
sku = {
'skuid': skuid,
'title': title,
'price': price,
"category_id1": category_id1,
'category_id2': category_id2,
'category_id3': category_id3,
'url': "https://item.m.jd.com/product/%s.html?m2wq_ismiao=1" % skuid,
}
skus.append(sku)
sku = {
‘skuid’: skuid,
‘title’: title,
‘price’: price,
“category_id1”: category_id1,
‘category_id2’: category_id2,
‘category_id3’: category_id3,
‘url’: “https://item.m.jd.com/product/%s.html?m2wq_ismiao=1” % skuid,
}
3.3. 詳細ページの分析
製品カルーセル、製品ポスター、製品ストア情報、製品紹介、仕様を入手する
1. 画像の分析 (商品カルーセル、ポスター)
画像の ID は提供されず、単に繰り返されるため、画像の名前を ID として使用して終了し、img_id を解析する関数をカプセル化します。
解析img_id code:
# 根据图片地址截取图片名称划取img的id(主键)
def splitImgId(src):
img_id = ''
list = src.split('/')
for i, li in enumerate(list):
if(li.find('.jpg', 0, len(li)) !=-1 or li.find('.png', 0, len(li)) != -1):
for key in li:
if(key == '.'):
break
img_id = img_id + key
return img_id
製品ポスター画像を入手 – 製品紹介:
注: これは非常にイライラします。長い間探していますが、画像がどこにレンダリングされているかがわかりません。後で、最終的に彼がスタイルの URL を取得したことがわかりました。
次に、自分で関数をカプセル化して背景 URL を取得します。
# 获取海报图imgList
def HB(html, driver):
imgs = []
img = ''
wait(driver, '//*[@id="detail"]')
style = str(html.xpath('//*[@id="commDesc"]/style/text()'))
style = style.split('background-image:url(')
for item in style:
for i, key in enumerate(item):
if (item[0] == '/' and item[1] == '/'):
if (item[i] == ')'):
if (img != ''):
imgs.append(img)
img = ''
break
img = img + item[i]
return imgs
画像コードを解析します:
# 1.解析图片(包括商品的轮播图,海报图)
def detail_img(html, goods_id, driver):
# 解析商品轮播图
imgs = []
flag = wait(driver, '//*[@id="loopImgUl"]/li')
if(flag == 1):
return imgs
lis = html.xpath('//*[@id="loopImgUl"]/li')
product_id = goods_id
is_master = 0
for i, li in enumerate(lis):
pic_url = li.xpath('./img/@src')[0]
product_pic_id = splitImgId(pic_url)
pic_desc = '商品轮播图'
pic_status = 1
# 主图,爬取的第一个为
if (i == 0):
is_master = 1
img = {
'product_id': product_id,
'is_master': is_master,
'pic_url': pic_url,
'pic_status': pic_status,
'product_pic_id': product_pic_id
}
imgs.append(img)
# 解析商品海报图
for item in HB(html, driver):
pic_url = item
product_pic_id = splitImgId(item)
pic_status = 1
pic_desc = "商品海报图"
img = {
'product_id': product_id,
'is_master': is_master,
'pic_url': pic_url,
'pic_status': pic_status,
'product_pic_id': product_pic_id
}
imgs.append(img)
return imgs
2. 店舗情報の取得
これについては何も言うことはありません。単純です。唯一のことは、実際には、プログラムエラーにつながる間違ったショップがいくつかあることが判明するということです。
そこで、待機する表示を追加しました。20 秒間彼を待ちます。ストアの要素が見つからない場合は、ネットワークの速度が原因ではない可能性がありますが、そのようなストアはありません
shop = {}
# 显示等待---封装函数
flag = wait(driver, '//*[@id="shopBaseInfo"]/div[2]/p[1]')
if(flag == 1):
return shop
try:
img = html.xpath('//*[@id="shopLogoInfo"]/span/img/@src')[0]
shop_name = html.xpath('//*[@id="shopInfo"]/p/span/text()')[0]
fan = html.xpath('//*[@id="shopBaseInfo"]/div[1]/p[1]/text()')[0]
goods_num = html.xpath('//*[@id="shopBaseInfo"]/div[2]/p[1]/text()')[0]
except:
return {}
shop = {
'img': img,
'shop_name': shop_name,
3. 仕様パラメータの取得
これは何も言うことはありません。データを保存するだけです。通常はエラーは発生しませんが、キャプチャ例外を追加できます。
# 有问题,就返回空字符串,程序继续执行
try:
specification = {
'specification': spec_param(driver)}
except:
specification = {
'specification': ''}
try:
sub_title = {
'sub_title': html.xpath('//*[@id="itemName"]/text()')[0]}
except:
sub_title = {
'sub_title': ''}
残りは分析したデータを統合、処理、保存するだけなので、あまり多くは紹介しません。
4. 部分的な結果の表示
1. category1 の第 1 レベルの分類では大カテゴリを 5 つだけ選択し、第 3 レベルの分類では各製品を 14 個だけ選択しました。7 時間以上かかりました。非常に苦痛を感じました。コード作成には適切な規律を考慮してください。
結果として抜け穴が多く、余計な時間がかかってしまいましたが、プロジェクト完了後、コードの最適化を行い、マルチスレッドクローリングの利用を検討しました。 1.5時間まで。
2. 分析、作成、バグの修正、このコードの実行、間違いと修正に 3 日かかり、かなり苦労しましたが、最終的には成功しました。
とても勉強になりました、もしこの記事を偶然見つけた方、もっと良い解決策があればアドバイスをお願いします。一緒に改善していきます。。。。
以上が今日私が共有したいことです、笑!至らない点があることも承知しておりますが、気にしないでいただければ幸いです。
ご質問がある場合、またはこのスクリプトが必要な場合は、バックグラウンドで私にご連絡いただくか、私を追加していただくか、技術交換グループに参加して一緒に議論し、学習することもできます。
さあさあ!!明日はきっと今日のお疲れ様ですよ!!
5. 次のエピソードのプレビュー
あはは、以前に多くのファンが私に大学のコンテストプロジェクト(中国コンピュータデザインコンテストの優勝、ファーウェイクンペンコンテストの優勝)が欲しいと個人的にメッセージをくれたので、それは私の秋の採用の主要プロジェクトの一つでもあるからです。インターンシップ。
前に時間をかけてお伝えしたかったのですが、最近はプロジェクトが忙しくて時間が取れないので、次号でシェアしたいと思います。。
前に時間をかけてお伝えしたかったのですが、最近はプロジェクトが忙しくて時間が取れないので、次号でシェアしたいと思います。。
プロジェクト部分のスクリーンショット:
プロジェクトや Python スクリプトが必要な場合は、公式アカウントをフォローして返信してください [jd_spider] [人工知能に基づくスマート キャンパス アシスタント]
最後に、この記事が良いと思う学生は、忘れずにこの記事を Xiaolong に転送し、もう一度読み、より多くの学生と共有する必要があります。
こんにちは、私はシャオロンです。過去 2 冊の本で BAT への反撃に成功したかわいい新人プログラマーです。
公式アカウント:誰もがBATに完璧に反撃できるように設計されたオリジナルのテクニカルアカウント。JAVA技術、MySQL、Redis、分散、ネットワークプランニング、OSなどの共有を中心に、大手メーカーの面接質問や大手メーカーの社内推奨事項を詳しく解説するほか、「第2弾 逆襲BAT体験記」やオーナーが経験した秋採用人数】面接メモ【。そしてオーナー様の【投资之道】
コメントもございます【副业之法】
!
公式アカウントに注目して、Xiaolong と一緒にもっと楽しいことを探索してください。今すぐ注意して、バックグラウンドで[大昌インタビュー] に返信して、大昌に当てるのに役立つ [ブティック インタビューメモ] を受け取りましょう! !, [人工知能に基づくスマートキャンパスアシスタント] に返信して、アカウントマスターの秋採用面接ブティックプロジェクトを受け取ります。
過去の素晴らしいレビュー:
Ali は、「MySQL の自動インクリメント主キーが連続していないのはなぜですか?」という質問でほとんど迷っていました。
「インタビューノート」 - JVMの終焉(ぶら下がりシリーズ)