Scraping 5ch using BeautifulSoup - python

For a research study, I am building a scraper using BeautifulSoup to collect content from the Japanese message board website 5ch. Currently, my code scrapes the thread titles, and the ID and Date that correspond to each post under each thread; however, it is unsuccessful at scraping the content of each post. Instead of scraping the text for each that was post, it scrapes every piece of text in entire thread.
My code is below. The issue is with the content variable. I want to scrape only the text of each post and right now it's scrapping all the text in the thread when I use ...find('dd'). Additionally, there does not seem to be a tag at a lower level than 'dd' (besides "font") so I am not sure what to use to just scrape the text of each post.
board_name = 'campus'
upperframe=[]
for board in range(1):
print('processing: {} board from 5Chan'.format(board_name))
url = 'https://kizuna.5ch.net/'+board_name+'/?v=pc'
print(url)
headers = {'user-agent': 'Chrome/104.0.0.0'}
try:
page=requests.get(url, headers=headers)
page.encoding = 'shift_jis'
except Exception as e:
error_type, error_obj, error_info = sys.exc_info()
print ('ERROR FOR LINK:',url)
print (error_type, 'Line:', error_info.tb_lineno)
continue
time.sleep(5)
soup=BeautifulSoup(page.text,'html.parser')
frame=[]
links=soup.find_all('div',attrs={'class':'THREAD_CONTENTS'})
print(len(links))
posts = []
Thread_titles =[]
contents = []
for j in links:
Thread_title = j.find("h3",attrs={'class':'thread_title'}).find('span').text.strip()
Thread_titles.append(Thread_title)
post = j.find("dl",attrs={'class':'thread'}).find('dt').text.strip()
posts.append(post)
string = ''.join(posts)
IDs = re.findall(r'ID:[A-Za-z0-9/+]+',string)
dates = re.findall(r'\d{4}/\d{2}/\d{2}',string)
content = j.find("dl",attrs={'class':'thread'}).find('dd').text.strip()
contents.append(content)
frame.append((Thread_titles,IDs,dates, contents))
upperframe.extend(frame)
Any suggestions would be very much appreciated!!

I created a script to scrape your website. The problem in your script was that the site has invalid html so the browser has to be really smart to figure out how to display the data (so in dev tools the tags are not parsed as the beautiful soup parses it).
How Firefox parses the tags:
<dl ...>
<dt ...></dt>
<dd ...></dt>
<dt ...></dt>
<dd ...></dt>
<dt ...></dt>
<dd ...></dt>
...
</dl>
How BeautifulSoup parses the tags:
<dl ...>
<dt ...>
<dd ...>
<dt ...>
<dd ...>
...
</dd>
</dt>
</dd>
</dt>
</dl>
So I created a code that removes extra dt or dd in the tag (the garbage variable).
import requests
from bs4 import BeautifulSoup
import re
session = requests.Session()
session.headers.update({"user-agent": "Chrome/104.0.0.0"})
for board in ["campus"]:
print(f"Processing {board} board")
url = f"https://kizuna.5ch.net/{board}/?v=pc"
page = session.get(url)
page.raise_for_status()
soup = BeautifulSoup(page.content, "html.parser")
threads = soup.find_all("div", class_="THREAD_CONTENTS")
print(f"Found {len(threads)} threads.")
threads_data = []
for thread in threads:
thread_title = thread.find("h3", class_="thread_title").text.strip()
#print(thread_title)
posts_data = []
# Find all posts container
posts = thread.find("dl", class_="thread").find_all("dt")
for post_header in posts:
# Copy posts header object to work with only one post at time
post_header = post_header.__copy__()
# Find post content
if post_content := post_header.find("dd"):
# Remove other posts due to bad page formating
if garbage := post_content.find("dt"):
garbage.decompose()
post_content = post_content.text.strip()
else:
post_content = ""
# Find the title of post
if post_title := post_header.find("b"):
post_title = post_title.text.strip()
else:
post_title = ""
post_header_text = post_header.text
# Find the id of post
if post_identify := re.search(r"ID:([a-zA-Z0-9\+]+)", post_header_text):
post_identify = post_identify.group(1)
else:
post_identify = ""
# Find the date of post
if post_date := re.search(r"(\d{4}/\d{2}/\d{2})", post_header_text):
post_date = post_date.group(1)
else:
post_date = ""
posts_data.append((post_title, post_identify, post_date, post_content))
threads_data.append([thread_title, posts_data])
print()
print("="*10, "result", "="*10)
print(threads_data)
Scraped data is in following structure:
[ # threads data
[ # thread 1
"thread 1 title",
[ # posts data
("post title", "post id", "post date", "post content") # post 1
("post title", "post id", "post date", "post content") # post 2
("post title", "post id", "post date", "post content") # post 3
...
]
],
[ # thread 2
"thread 2 title",
[ # posts data
("post title", "post id", "post date", "post content") # post 1
("post title", "post id", "post date", "post content") # post 2
("post title", "post id", "post date", "post content") # post 3
...
]
]
]
Outputs:
[['はやちゃんの恋心にきゅんとする恋愛アニメ「イジらないで!長瀞さん」のスレ。', [('学生さんは名前がない', '3tOLmZNI0', '2023/01/31', 'はやちゃん公式。 http://www.nagatorosan.jp/'), ('学生さんは名前がない', '3tOLmZNI0', '2023/01/31', 'は? 恒一くんは、いざというときにはお姫さま(鳴ちゃん)を守るナイトになる強い男だし。'), ('学生さんは名前がない', 'AAri6czSa', '2023/01/31', 'http://i.imgur.com/AUBeUU2.jpg'), ('学生さんは名前がない', 'ONaPVWoj0', '2023/01/31', 'ぜろよんくん、そういう可哀想な描写はやめなさい。'), ('学生さんは名前がない', '7K2aLB9+a', '2023/02/01', 'おかしいなぁ、ジャンル一致してるとおもうけどなぁ'), ('学生さんは名前がない', 'QVy3P6Su0', '2023/02/01', 'ぜろよん「センパイ首吊り描きてぇwwwオラッ戻って来い774!wwww」 先生「・・・・・『鈴木健也』」ボソッ ぜろよん「は、はひっ・・・」 http://i.imgur.com/a7oANIK.jpg http://i.imgur.com/OHlZtkf.png'), ('学生さんは名前がない', 'iKDy8dCC0', '2023/02/01', '裏の世界から這い上がった人代表だよね、ナナシ先生 例えば声優でいえば種崎さんみたいな…'), ('学生さんは名前がない', 'L4EavW3z0', '2023/02/01', '今まで恥骨を破壊したキャラのことなど忘れ安穏とした生活を送る774……'), ('学生さんは名前がない', 'sM6sw8Jk0', '2023/02/01', '帝国ホテル前、 自家用のロールスロイス・ファントムへと向かう774とその一行。 774「電通の部長との打ち合わせが思いのほか早く済んだね」 秘書「8時からはトゥールダルジャンで、日本テレビの社長と会食です」 774「ふう、アニメ化が決まってからはしょっちゅうレストランで会食だな。太らなければいいんだが……」 ゼニアの高級スーツに包まれた腹を大儀そうになでる774。 その腹を狙うかのように、物陰から走り寄った金髪の少女が、黒い斧を振りかぶる。 だが、その足取りは、まるで恥骨と膣を破壊されてでもいるかのように不確かで、 あっさりと少女は、ボディーガードの丸太のような腕にねじ伏せられた。 774「何者だ?\u3000狙われる心当たりはそれなりにあるが……君の顔は見覚えがないな」 少女「ふざけないで……!\u3000私となのはのおまんこを殺したこと、忘れたとは言わさない!\u3000木ノ下香奈枝さんも、おっぱいをうんこやお寿司に改造された人たちも……!!」 774「何のことかさっぱり分からんよ。まったく、正統派一流恋愛漫画家の私の耳に、ずいぶんと下卑た言葉を聞かせてくれる。丸太君、その虫ケラの処分は頼むよ」 丸太腕「お任せください、ボス」 怨念と憤怒とに満ちた少女の絶叫を気にかける素振りもなく、シンジとアスカの幸福な家庭を車体に描いたロールスロイス・ファントムは、颯爽と走り去った。'), ('学生さんは名前がない', 'RgAMzJvK0', '2023/02/01', 'アニメ3期+OVAまで確定しているのに 生乾きのシャツを着ながら女の子の首を締めるユキヲを見習えや…'), ('学生さんは名前がない', '1I6rkhkQ0', '2023/02/01', 'ナナシ先生、はやちゃん連載し始めのころとだいぶ絵が変わったねーっ! 今のはやちゃんも可愛い!!')]], ['私立蠟梅学園中等部の用務員', [('学生さんは名前がない', 'kIrF', '2022/10/23', '明日「おはようございます用務員さん!今日もがんばっていきましょー!オスッ!」'), ('学生さんは名前がない', 'Xlj+39M0r', '2022/12/26', 'クリスマス明けの処女膜チェックするから全員あの画像みたくおまんこ見せろ!'), ('学生さんは名前がない', 'yRjq9DhKp', '2023/01/02', 'ほしゅー'), ('学生さんは名前がない', '7Msfi0y70', '2023/01/03', '兎原、お年玉全部持ってこい…'), ('学生さんは名前がない', 'b67phXpCd', '2023/01/03', '>>70 全員のおまんこ舐め比べして目隠しで当てられるようになりたい'), ('学生さんは名前がない', 'wlIboTMKp', '2023/01/06', 'ビコミチ http://pbs.twimg.com/media/FlsS6HCacAANnkn.jpg'), ('学生さんは名前がない', 'dRHQT4sDH', '2023/01/11', '>>70 神定期'), ('学生さんは名前がない', 'jEA1PERwp', '2023/01/22', 'ほしゅー'), ('学生さんは名前がない', 'zMv3pOOTp', '2023/01/23', 'かぜおこし http://pbs.twimg.com/media/FnFWkHZacAMzghF.jpg'), ('学生さんは名前がない', 'U1Yw217Q0', '2023/01/26', 'ハァハァ…兎原さんはもう生理きてるのかな…'), ('JPW', 'wT0lZtYXH', '2023/02/01', 'てすと')]], ['着物貧乳女', [('学生さんは名前がない', 'G1ssGhtn0', '2023/01/22', '前スレ http://kizuna.5ch.net/test/read.cgi/campus/1671809440/'), ('学生さんは名前がない', '7RBJu', '2023/02/01', '「にち」って処女のキツマンにレイプ魔の丸太ちんぽが無理やり入る音みたいだよね'), ('学生さんは名前がない', 'duIAYC590', '2023/02/01', 'にちかのキツマン VS パワー系ヴーの丸太ちんぽ'), ('学生さんは名前がない', 'T5DpV9Odd', '2023/02/01', '>>223 ワロタwww'), ('学生さんは名前がない', 'uF4Ho02Jd', '2023/02/01', 'にちかってほんと妥協の果てって感じだよね'), ('学生さんは名前がない', 'L4EavW3z0', '2023/02/01', 'ぼくも本当は園田さんとかが良いけど……'), ('学生さんは名前がない', 's+JieWq50', '2023/02/01', 'ふゆこか着貧乳がいい…'), ('学生さんは名前がない', '3mFEt7h0a', '2023/02/01', 'http://i.imgur.com/HOEIOKm.png'), ('学生さんは名前がない', 'YM00he72a', '2023/02/01', '僕は甘奈ちゃんか透がいい'), ('学生さんは名前がない', 'ydfKvrqA0', '2023/02/01', '着物貧乳女VSキモい貧乏男'), ('学生さんは名前がない', '1l2N2Svd0', '2023/02/01', '>>232 アイちゃん!')]], ['ゴミ拾いなんて用務員にさせればいいでしょう?早く行かないと新作スイーツが売り切れてしまいますわw', [('学生さんは名前がない', 'bMI6WXYEa', '2023/01/14', 'くづ 前スレ ターボさんを見ていると、障害児を健常者のクラスに入れたがる親のエゴの愚かしさを感じますわw http://kizuna.5ch.net/test/read.cgi/campus/1669984394/'), ('学生さんは名前がない', 'zbWHj5IFd', '2023/01/31', '早くスマホから出てきて僕を助けろ!!! http://i.imgur.com/VnBdHDq.jpg'), ('学生さんは名前がない', 'sXORnmV3a', '2023/02/01', 'うう…ミークちゃんに顔の前で放屁されてパンツにたくさん射精したいよ…'), ('学生さんは名前がない', '7RBJu', '2023/02/01', 'ダイヤ、婚姻届と指輪を用意して待ってるからな'), ('学生さんは名前がない', 'zJIQ5CNH0', '2023/02/01', 'http://pbs.twimg.com/media/Fnds9JYaQAAKvZC.jpg'), ('学生さんは名前がない', 'VTfDwk0Hd', '2023/02/01', '確認してあげたい http://i.imgur.com/uwA4aFh.jpg'), ('学生さんは名前がない', 'U7ZWAb6O0', '2023/02/01', 'スズカさんがフクと仲良いの”アレ”っぽくて好き'), ('学生さんは名前がない', 'P89zo3Og0', '2023/02/01', '2期のウララ枠はターボ'), ('学生さんは名前がない', 'ydfKvrqA0', '2023/02/01', '>>466 ブーツ脱ぎたて蒸れ蒸れ素足のにおいをかいで股関にジワァ……と滲みをつくる(∵)トレーナー'), ('学生さんは名前がない', 'kXm9djM2d', '2023/02/01', 'イケメン丸太トレーナーだからさせてるのであって用務員トレーナーにはさせないでしょ…'), ('学生さんは名前がない', '7RBJu', '2023/02/01', 'ドトウの爆乳を手首周り14cmの俺の腕で乱暴に揉みしだいて泣かせてやるからな…')]], ['■□◆◇●○★☆\u3000神乃巣其六十三\u3000☆★○●◇◆□■', [('純正神', 'rm4OfOMF0', '2022/11/27', '新スレの周期だよ~'), ('煌星乃神', 'TJtOflcH0', '2023/01/31', 'UMA娘ですよ~'), ('純正乃神', '4x23Kdld0', '2023/02/01', 'わたしの周期~ 山手線がグモで大変だったよ~'), ('', 'NWyQjh8pp', '2023/02/01', 'ウグゥッチュイイイイン'), ('( ・◇・)', 'T5DpV9Odd', '2023/02/01', '神なのにグモガチャ確変できない・・・だと・・・!?'), ('雷天使プラジネット', '4x23Kdld0', '2023/02/01', '>>553 禁句ハ制裁イタシマス 撲滅奥義1732 『居合雷敬礼』'), ('煌星乃神', '4x23Kdld0', '2023/02/01', '>>554 アシダカグモですよ~'), ('', 'PDhwoKg', '2023/02/01', '落寸号令雷ですよ~'), ('雷天使プラジネット', '4x23Kdld0', '2023/02/01', '>>557 撲滅奥義0155 『落扇魔光弓』 莫大ナ雷力ヲ光子変換シ、撃墜スルノデス\u3000トルノデス'), ('', '4mVNdpW', '2023/02/01', 'Lusting God laid lightですよ~'), ('純正乃神', '4x23Kdld0', '2023/02/01', '>>559 様々なモンスターが滅茶苦茶に融合し、ヤバそうな生物、フキンシーンが登場したよ~')]], ['結婚・妊娠適齢期の女性作家ちゃんのスレ', [('学生さんは名前がない', '3+u', '2023/01/29', '前スレ 本当に信じられる女性作家たち http://kizuna.5ch.net/test/read.cgi/campus/1664627045/'), ('学生さんは名前がない', 'K1g6NRDy0', '2023/01/30', '>>29 ツイッタ復活してたの!!?!'), ('学生さんは名前がない', 'tzQ0CgUQ0', '2023/01/30', 'アズちゃんもラッコみたいなことして欲しい'), ('学生さんは名前がない', 'qMpw9X+1M', '2023/01/30', '【手品先輩】アズ総合 Part16【賢者が仲間になった】 http://jbbs.shitaraba.net/bbs/read.cgi/comic/7166/1674309033/'), ('学生さんは名前がない', 'orP8j9g6a', '2023/01/30', 'http://i.imgur.com/mwu3346.jpg'), ('学生さんは名前がない', 'fq', '2023/01/30', '>>31 まだにゃん 今月のMAXに載ってるかもと思ってたら無かったにゃんw'), ('学生さんは名前がない', 'O9s7UbXr0', '2023/01/30', 'きんモザで男シノにブチギレた丸太編集から優男編集に変わってそうで心配だよ……'), ('学生さんは名前がない', 'JOXoI6lUM', '2023/01/30', 'あずねえ'), ('学生さんは名前がない', 'v+zF+Otd0', '2023/01/31', 'あずねえを養いたい'), ('学生さんは名前がない', '0RyiiwzT0', '2023/02/01', '真夏の和歌山のボロアパートであずねえと汗だくセックスしたい'), ('学生さんは名前がない', 'AHX3', '2023/02/01', 'ラッコの模写でイク')]], ['ぼっち(∵)(∵)(∵)(∵)・ざ・ろっく!', [('学生さんは名前がない', 'mOOjASszd', '2023/01/19', '前スレ ぼっち(∵)(∵)(∵)・ざ・ろっく! http://kizuna.5ch.net/test/read.cgi/campus/1672368716/'), ('学生さんは名前がない', '1vtnuFG+d', '2023/02/01', 'fc2で見慣れてるせいでたたない大学生(既卒)'), ('学生さんは名前がない', 'sjwetbupp', '2023/02/01', '後藤はぼくの!!!'), ('学生さんは名前がない', 'dshd4d51d', '2023/02/01', '汚物 http://i.imgur.com/gTbhdg5.jpg'), ('学生さんは名前がない', 'vjbrHM+tM', '2023/02/01', '汚仏'), ('学生さんは名前がない', 'oPddxL6ya', '2023/02/01', 'お持ち帰りして熱湯ぶっかけて'), ('学生さんは名前がない', '5BOeMQS00', '2023/02/01', '>>445 「終電に乗り遅れたお姉さんを持ち帰りハメちゃいました/廣井きくり」 駅のホームでおもらしして眠りこけているお姉さん。どうやら泥酔して最終電車に乗り遅れたようだ。 介抱するつもりだったんだけど、ぐだぐだに酔ってるのを見ていたらイタズラしたくなり俺の家に運んだ。 キャミワンピをまくりあげ、おっぱいを揉んでもマンコを刺激してもまったく起きる気配がないので 寝ている間にチンポをブチ込んじゃいました。ごちそうさま~♂♂♂ #素人 #持ち帰り #潮吹き #中出し #生ハメ・生姦 #バイブ #フェラ #1080p #60fps'), ('学生さんは名前がない', 'OgHwbodT0', '2023/02/01', '>>445 廣井さぁ、お漏らしとかお前いくつだよ?www'), ('学生さんは名前がない', 'IDlvELtk0', '2023/02/01', '廣井って酔い潰れた時のガードの緩さ以外に良い所ある?'), ('学生さんは名前がない', '3J3aLR3q0', '2023/02/01', 'PAさん、好きだ'), ('学生さんは名前がない', 'OFfeMJNx0', '2023/02/01', 'パンツずらされてるし、既に一発ヤラれたあとでしょこれ…。')]], ['10年に一度の大寒波が来るから仕事辞めたい大学生(既卒)', [('学生さんは名前がない', 'PVx6TK2da', '2023/01/22', 'ぶるっちょさむさむ'), ('学生さんは名前がない', 'nTYCu692a', '2023/02/01', 'みんな、ちゅらいね…。なでなでぎゅっ。'), ('学生さんは名前がない', 'GXm6yqaS0', '2023/02/01', '東京って職質多いらしいね 田舎だから一回もされたことないよ'), ('学生さんは名前がない', 'Q4UoG2p', '2023/02/01', 'ぼき短期間に何度も職質されてムカついたから何回聞いてくんねんアホンダラ履歴とか取ってないんかい暇人が!!って怒鳴り散らかしちゃったよ・・・'), ('学生さんは名前がない', 'fWjwWO7yd', '2023/02/01', '自転車乗ってる人はよく職質受けてるよね'), ('学生さんは名前がない', 'lTXs8dgid', '2023/02/01', '酒開けた しごとつらい'), ('学生さんは名前がない', 'On+H83xe0', '2023/02/01', '今まで職質受けたことないけど質問ある?'), ('学生さんは名前がない', 'Q4UoG2p', '2023/02/01', '大生民くんは動物にも嫌われてるんだよ? http://pbs.twimg.com/media/FnzkfPMaYAIBVgR.jpg'), ('学生さんは名前がない', 'fWjwWO7yd', '2023/02/01', '>>827 これワンワンはタバコのにおい嫌いだからでしょ・・・'), ('学生さんは名前がない', 'F7h5Vh8x0', '2023/02/01', '僕夜勤帰りでパン食いながらチャリ乗ってたらスマホ運転と間違えて止められて注意書みたいなの渡されたよ'), ('学生さんは名前がない', 'fWjwWO7yd', '2023/02/01', '世間の大生民差別がひどい!')]], ['スパイ教室', [('学生さんは名前がない', '0z+2kFR9d', '2023/01/27', 'http://spyroom-anime.com/ なぜたたないのか'), ('学生さんは名前がない', 'zlN26vlh0', '2023/01/27', 'せめてドスケベアニメなのか無スケベアニメなのかという情報くらいは提供していただかないと困りますね・・・・。'), ('学生さんは名前がない', '3T13', '2023/01/27', 'http://i.imgur.com/rAMW5Uj.jpg こういうアニメです あとは察してください'), ('学生さんは名前がない', 'oCSEdxiA0NIKU', '2023/01/29', '左のオンナァッッ!'), ('学生さんは名前がない', 'gEV6eOzCMNIKU', '2023/01/29', '最高のド雌豚、花園… パジャマからはち切れんばかりのおっぱいとお尻で、豚みたいに犯してくださいと全身で表現してる下品な雌豚め…'), ('学生さんは名前がない', '27toeRo80', '2023/01/30', 'ぼく、こういう自信満々なツラでご自慢のデカパイ強調してくるオンナに弱い。 http://i.imgur.com/7k7mRG5.jpg http://i.imgur.com/WsmnLnp.jpg http://i.imgur.com/cvup96g.jpg'), ('学生さんは名前がない', '9FMfQdtcd', '2023/01/30', '>>23 わからせてやりたいドヤ顔'), ('学生さんは名前がない', 'BpMIgwiAp', '2023/01/30', '>>23 こいつがめちゃくちゃにされるシーン当然あるよね?'), ('学生さんは名前がない', 'Y9vt68570', '2023/01/30', 'ラジオ体操やらせたい'), ('学生さんは名前がない', 't', '2023/02/01', 'リリスパみたいにはならないでほしい'), ('学生さんは名前がない', 'QVy3P6Su0', '2023/02/01', '今のところ、クソアニメ。')]], ['新約ヴースレ 続編 第3章', [('学生さんは名前がない', 'ZyWdjjokd', '2023/01/08', '前スレ ヴー!!最終章\u30003期決定!僕のママはどこ?編 http://kizuna.5ch.net/test/read.cgi/campus/1659710062/ 新約ヴースレ http://kizuna.5ch.net/test/read.cgi/campus/1661940155/ 新約ヴースレ 続編 http://kizuna.5ch.net/test/read.cgi/campus/1666011872/ 新約ヴースレ 続編 第2章 http://kizuna.5ch.net/test/read.cgi/campus/1669515888/'), ('学生さんは名前がない', 'XSn9LwbRp', '2023/02/01', 'ケツ毛にトイレットペーパーついてそう…'), ('学生さんは名前がない', 'zJIQ5CNH0', '2023/02/01', 'すきなAV女優だぞ'), ('学生さんは名前がない', 'L4EavW3z0', '2023/02/01', 'ヴー………お姉ちゃん貼れ!!'), ('学生さんは名前がない', 'ydfKvrqA0', '2023/02/01', 'http://i.imgur.com/du4h40p.png 端女ども! ご奉仕しろ!!!'), ('学生さんは名前がない', 'V8w49q630', '2023/02/01', 'これぼくだ… http://i.imgur.com/DUN3cU8.jpg http://i.imgur.com/BD6vcoF.png http://i.imgur.com/inE8yF3.png'), ('学生さんは名前がない', 'LESDx', '2023/02/01', 'ならばこれが僕 http://pbs.twimg.com/media/FnpGNBQaMAEpsGd.jpg'), ('学生さんは名前がない', 'lTXs8dgid', '2023/02/01', 'ぼくがいっぱいだ'), ('学生さんは名前がない', 'fWjwWO7yd', '2023/02/01', '>>567 ヴー!ヴー!!ヴーーー!!!・・・・♂♂♂♂♂'), ('学生さんは名前がない', '8Oy4p6i', '2023/02/01', '>>567 絵師教えろや・・・'), ('学生さんは名前がない', 'V8w49q630', '2023/02/01', '>>571 3枚目の右上♂ http://i.imgur.com/LYR75Y2.jpg')]]]

Related

Python and Starlette - receiving a tuple from an API that's trying to return json

I'm working on a Starlette API. I am trying to receive a response object or json but I end up with a tuple. Any thoughts or guidance will be appreciated.
Frontend:
headers = {"Authorization": settings.API_KEY}
association = requests.get(
"http://localhost:9999/get-association",
headers=headers,
),
print("association:", type(association))
association: <class 'tuple'>
Backend:
#app.route("/get-association")
async def association(request: Request):
if request.headers["Authorization"] != settings.API_KEY:
return JSONResponse({"error": "unauthorized"}, status_code=401)
# return JSONResponse(
# content=await get_association(), status_code=200
# )
association = {"association": "test data"}
print("association:", type(association), association)
return JSONResponse(association)
association: <class 'dict'> {'association': 'test data'}
You have a comma after requests.get. This is making a tuple of (<Response [200]>,).

Can not get cookie from localhost websocket fast api

I'm testing websockets to work with cookies and trying to get them in fast api. I manually installed them in chrome but I get an empty dictionary inside the application. I used the fast api documentation templates and slightly redesigned it
My html
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
<label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
<button onclick="connect(event)">Connect</button>
<hr>
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = null;
function connect(event) {
var itemId = "1000"
var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI2MjlmYmU3ZGNjMzQxMGFiNWE2MmZkOWYiLCJ1c2VyIjoiQmk4eVVhOG5TS1dFRm8weEJjYWkwRUtDU2E3TyJ9.7hE3qIcFoLLoDqQSliaHXhSPs4FW75fNafumPdKHPmI"
ws = new WebSocket("ws://localhost:8000/ws/" + itemId + "/?token=" + token);
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
event.preventDefault()
}
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
My websocket
class ConnectionManager:
def __init__(self):
self.active_connections: list = []
async def connect(self, websocket: WebSocket):
await websocket.accept()
self.active_connections.append(websocket)
def disconnect(self, websocket: WebSocket):
self.active_connections.remove(websocket)
async def send_personal_message(self, message: str, websocket: WebSocket):
await websocket.send_text(message)
async def broadcast(self, message: str):
for connection in self.active_connections:
await connection.send_text(message)
manager = ConnectionManager()
#app.websocket("/ws/{project_id}/")
async def test_websocket(websocket: WebSocket,
project_id: int,
token: str = Depends(authorization.get_user_websocket_token)
):
print(websocket.cookies)
await manager.connect(websocket)
try:
while True:
project = await db["storages"].find_one({"project_id": project_id})
if token in project["users"]:
print(True)
data = await websocket.receive_text()
await manager.send_personal_message(f"You wrote: {data}", websocket)
await manager.broadcast(f"Client says: {data}")
except WebSocketDisconnect:
manager.disconnect(websocket)
await manager.broadcast(f"Client left the chat")
print(websocket.cookies) returns an empty dictionary {}
Cookies are domain-defined, so you should point at localhost:8000 and there define that Cookie, not 127.0.0.1:8000.
Maybe it's easier to check them in Postman.
With such crafted request:
They will be available at websocket.cookies for sure.
websocket.cookies under the hood checks Cookie header:
#property
def cookies(self) -> typing.Dict[str, str]:
if not hasattr(self, "_cookies"):
cookies: typing.Dict[str, str] = {}
cookie_header = self.headers.get("cookie")
if cookie_header:
cookies = cookie_parser(cookie_header)
self._cookies = cookies
return self._cookies

Unable to make websocket connection in django

I am trying to make a websocket connection
I am using the django's channels API.
For some reason the handshaking is not taking place. Maybe it has something to do with the url?
This is my main routing.py file
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter,URLRouter
import chat.routing
application = ProtocolTypeRouter({
'websocket':AuthMiddlewareStack(
URLRouter(
chat.routing.websocket_urlpatterns
)
),
})
This is my chat app routing.py
from django.urls import re_path,path
# from django.conf.urls import url
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/room/(?P<room_name>\w+)/(?P<username>\w+)/$',consumers.ChatRoomConsumer.as_asgi()),
]
This is my chat app consumers.py file
# import pytest
from accounts.models import Account
from .models import ChatRoom, Message
from channels.generic.websocket import AsyncWebsocketConsumer
import json
from channels.db import database_sync_to_async
import string
import random
# from asgiref.sync import sync_to_async
class ChatRoomConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_name = self.scope['url_route']['kwargs']['room_name']
self.room_group_name= 'chat_%s'%self.room_name
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self,close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
username = text_data_json['username']
room_name = text_data_json['room_name']
# print("[RECIEVED] "+room_name)
print("[receiving....]"+message)
await self.channel_layer.group_send(
self.room_group_name,
{
'type':'chatroom_message',
'message':message,
'username':username,
'room_name':room_name,
}
)
async def chatroom_message(self,event):
message = event['message']
username = event['username']
room_name = event['room_name']
print("[SENDING....]"+message)
room_obj = ChatRoom.objects.get(room_name=room_name)
sender = Account.objects.get(username=username)
await self.save_message(message=message,sender=sender,room_name=room_obj)
await self.send(text_data=json.dumps({
'message':message,
'username':username,
}))
#database_sync_to_async
def save_message(self,message,sender,room_name):
chat_id = ''.join(random.choices(string.ascii_uppercase+string.digits,k=10))
new_message = Message(message_id=chat_id,message=message,room_name=room_name,message_sender=sender)
new_message.save()
This is my JavaScript
{{room_name|json_script:"room-name"}}
{{username|json_script:"user_username"}}
{% if room_user_1_username %}
{{room_user_1_username|json_script:"room_user_1"}}
{% endif %}
{% if room_user_2_username %}
{{room_user_2_username|json_script:"room_user_2"}}
{% endif %}
<script>
const roomName = JSON.parse(document.getElementById('room-name').textContent);
const username = JSON.parse(document.getElementById('user_username').textContent);
const room_user_1 = JSON.parse(document.getElementById('room_user_1').textContent);
const room_user_2 = JSON.parse(document.getElementById('room_user_2').textContent);
var input_box = document.getElementById("input");
if(input_box.innerHTML==""){
document.getElementById("submit").style.visibility=false;
}
else{
document.getElementById("submit").style.visibility=true;
}
//for sending the message to websocket
document.getElementById("submit").onclick=function(e){
const messageInputDom = document.querySelector("#input");
const message = messageInputDom.value;
console.log("message: "+message);
console.log("username:"+username);
current_user = "{{request.user.username}}";
console.log("current_user: "+current_user);
if(username==room_user_1 || username==room_user_2){
if(messageInputDom.value!=""){
chatSocket.send(JSON.stringify({
'message':message,
'username':username,
'room_name':roomName,
}));
}
messageInputDom.value = '';
}
else{alert("Unauthorised access to a chat room");console.log("Message could not be sent!");}
messageInputDom.value='';
}
//setting up the websocket
const chatSocket = new WebSocket(
'ws://'+
window.location.host+
'/ws/chat/room/'+
roomName+
'/'+
username+
'/'
);
//getting messages from the websocket
chatSocket.onmessage = function(e){
const data = JSON.parse(e.data);
location.reload();//ON GETTING NEW MESSAGES
console.log("data: message->"+data.message+"\nusername->"+data.username);
$("#text-msg").val('\n'+data.username+": "+data.message+' (just now)');
alert(data.message);
}
</script>
It should show something like "CONNECT handshaking" on the cmd

I get status code 300 while updating the space information and image

i have been trying to update the space information along with the images that is on the same template.I could add the content but could not update.i tried to pass slug while submitting for updated space information for updating images too but the slug is shown null. My ajax code and view was working for the add part but not for update part. I get my console.log('rent worked') printed that is inside success function but 'request.post' is not printed which is inside EditSpaceView function What might be the reason? What should i do to make it work ?
views.py
class EditSpaceView(View):
def post(self,request,*args,**kwargs):
print ('edit space view',request)
if request.POST:
print('request.post')
response = HttpResponse('')
print('edited owner name is',request.POST.get('ownerName'))
print('edited amenities',request.POST.get('amenities'))
rental = Rental.objetcs.get(slug=self.kwargs['slug'])
rental.ownerName = request.POST.get('ownerName')
rental.email = request.POST.get('email')
rental.phoneNumber = request.POST.get('phoneNumber')
rental.listingName = request.POST.get('listingName')
rental.summary = request.POST.get('summary')
rental.property = request.POST.get('property')
rental.room = request.POST.get('room')
rental.price = request.POST.get('price')
rental.city = request.POST.get('city')
rental.place = request.POST.get('place')
rental.water = request.POST.get('water')
rental.amenities = request.POST.get('amenities')
rental.save()
response['pk-user'] = rental.slug
#response['pk-user'] = rental.pk
print('response slug', response);
return response
return HttpResponseRedirect('/')
class EditImage(View):
model = Rental
template_name = 'rentals/rent_detail.html'
def get(self, request, *args, **kwargs):
return render(request, self.template_name)
def post(self,request,*args,**kwargs):
try:
rental = Rental.objects.get(slug = self.kwargs['slug'])
print('rental slug',rental)
except Rental.DoesNotExist:
error_dict = {'message': 'Rental spae not found'}
return self.render(request,'rentals/rent_detail.html',error_dict)
if request.FILES:
for file in request.FILES.getlist('image'):
print('file',file)
image = Gallery.objects.create(rental = rental, image=file)
print('image',image)
return HttpResponseRedirect('/')
urls.py
url(r'^edit/image/(?P<slug>[\w-]+)/$', EditImage.as_view(), name="editImage"),
url(r'^edit/space/(?P<slug>[\w-]+)/$', EditSpaceView.as_view(), name="editSpace"),
ajax code
$.ajax({
url:'/edit/space/'+this.props.slug+'/',
contentType: "application/json",
data:sendData,
type:'POST',
success: function(data, textStatus, xhr ) {
console.log('rent worked');
var slug = xhr.getResponseHeader('pk-user');
console.log('slug',slug);
$.ajax({
url:'/edit/image/'+slug+'/',
data:image,
contentType:false,
processData:false,
type:'POST',
mimeType: "multipart/form-data",
success: function(data) {
console.log('success');
}
});
}
});
Stacktrace in server console
[05/Apr/2016 02:58:35] "GET /api/v1/rental/tushant-khatiwada/ HTTP/1.1" 200 963
edit space view <WSGIRequest: POST '/edit/space/tushant-khatiwada/'>
[05/Apr/2016 02:58:50] "POST /edit/space/tushant-khatiwada/ HTTP/1.1" 302 0
[05/Apr/2016 02:58:51] "GET / HTTP/1.1" 200 2363
Not Found: /edit/image/null/
[05/Apr/2016 02:58:51] "POST /edit/image/null/ HTTP/1.1" 404 6882
[05/Apr/2016 02:58:35] "GET /api/v1/rental/tushant-khatiwada/ HTTP/1.1" 200 963
edit space view <WSGIRequest: POST '/edit/space/tushant-khatiwada/'>
[05/Apr/2016 02:58:50] "POST /edit/space/tushant-khatiwada/ HTTP/1.1" 302 0
[05/Apr/2016 02:58:51] "GET / HTTP/1.1" 200 2363
These requests suggests that editing with EditSpaceView worked as expected. The 300 response is expected and due to HttpResponseRedirect("/") at the end of that view.
The request
[05/Apr/2016 02:58:51] "POST /edit/image/null/ HTTP/1.1" 404 6882
suggests that slug that you are using to build the URL for ajax request is not valid or is null. Specifically check this line,
var slug = xhr.getResponseHeader('pk-user');

Receiving 500 error in iphone application because mismatch of data formats

I am developing an iPhone application which uses Django as the backend. Web services being developed in Django REST framework post results, Django developer use postman extension to test whether data is actually getting posted.
For example one web service is as follows
class EventCommentPost(generics.ListCreateAPIView):
"""Post a comment to particular Event"""
# permission_classes = (permissions.IsAuthenticated,)
model = Event_Comment
serializer_class = EventCommentSerializer
def post(self,request,*args,**kwargs):
user_name = request.POST['user_name']
comment = request.POST['comment']
event_id = request.POST['event_id']
UserData = User.objects.get(id = user_name)
EventData = Event.objects.get(id = event_id)
Comment = Event_Comment(user_name = UserData,comment =comment,event_id=EventData)
Comment.save()
event_comment_id = Event_Comment.objects.get(user_name = UserData,comment =comment,event_id=EventData)
notification_data = Notification(user_id = EventData.Event_Creator,activity_type = 'Comment',source_id=event_comment_id.id)
notification_data.save()
serializer = EventCommentSerializer(event_comment_id)
list = []
list.append(serializer.data)
return Response(list)
Now I am using that webservice in my app as follows..
-(void)getDataWithBlokc:(OnComplate)block :(NSString*)strUrl data:(NSDictionary*)dictData
{
NSLog(#"POST DATA:=====%#",dictData);
NSString *hostURL=[NSString stringWithFormat:#"%#%#",HOST_NAME,strUrl];
NSURL *url=[[NSURL alloc] initWithString:hostURL];
NSData *jsonData;
NSString *jsonString;
if ([NSJSONSerialization isValidJSONObject:dictData])
{
jsonData=[NSJSONSerialization dataWithJSONObject:dictData options:0 error:nil];
jsonString=[[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:jsonData];
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%lu", (unsigned long)[jsonData length]] forHTTPHeaderField:#"Content-Length"];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError)
{
NSDictionary *dict=[NSDictionary dictionary];
NSInteger httpStatus = [((NSHTTPURLResponse *)response) statusCode];
NSLog(#"httpStatus inside block:%ld",(long)httpStatus);
if ([data length]>0 && connectionError==nil)
{
dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:&connectionError];
block(dict);
}
else if (connectionError)
{
block(dict);
}
}];
}
but this line I am getting 500 status code because of mismatch of data formats
dict=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers|NSJSONReadingAllowFragments error:&connectionError];

Categories