Requests + BeautifulSoup で html から form データの雛形作成他
Requests と BeautifulSoup は忘れた頃に使って何度も調べるのでメモ。小ネタ。
Requests を使ってログイン処理をするときはだいたいこんな処理をかく。
s = requets.Session() r = s.get('http://example.com/login') soup = BeautifulSoup(r.content, 'html.parser') payload = { 'id': 'id', 'password': 'password' } s.post('http://example.com/login', data=payload)
payload の部分がこれくらいシンプルならいいけど、csrf token とかをつけて送る場合に BeautifulSoup を使って token 部分を抜き出す必要がある。そんなときはだいたい以下のようなコードを書いて payload の雛形を作る
内包表記ver
payload = { input['name']: input['value'] if input.has_attr('value') else '' for input in soup.select('form input') if input.has_attr('name') }
for でシンプルに書くとこんな感じ
payload = {} for input in soup.select('form input'): if not input.has_attr('name'): continue value = input['value'] if input.has_attr('value') else '' payload[input['name']] = value
あとはこの payload に適当に id/pass を詰め込んで post すればよい。
その他メモ
- BeautifulSoup はdict の ように in を使って要素があるか問い合わせてはダメ('value' in input みたいな)。has_attr を使う。
- Cookie とか使うなら Session を使う。
- Session の save / load は pickle でいけそう
import requests, requests.utils, pickle session = requests.session() with open('somefile', 'w') as f: pickle.dump(session, f) with open('somefile') as f: session = pickle.load(f)
- findAll ? find_all ? どっちやねん > find_all らしい (最近のpythonのスタイルに従う)
AtCoder ABC 105 D
python に慣れるため久しぶりにプロコンをやってみた。今の AtCoder になってから初だと思う。
D: Candy Distribution - AtCoder Beginner Contest 105 | AtCoder
code
from collections import defaultdict N, M = map(int, input().split()) A = list(map(int, input().split())) Amod = defaultdict(int) sum_mod = 0 for v in A: sum_mod = (sum_mod + v) % M Amod[sum_mod] += 1 sum_mod = 0 ans = 0 for v in A: ans += Amod[sum_mod] sum_mod = (sum_mod + v) % M Amod[sum_mod] -= 1 print(ans)
emacs vue-mode で構文チェックする領域がおかしくなったの対処方
vue-mode で書いていると構文チェックする領域がおかしくなるときがある。vue-mode の、というより mmm-mode で発生する現象なのかもしれない。
具体的には
Vue で動的にコンポーネントを追加
Vue で動的にコンポーネントを追加したい
よくある TODO アプリで言うと、ボタンを押したらTODO を追加したい。
UIとしたらこんな感じ
TODO追加ボタンを押すたびにコンポーネントを追加したい。
new するやり方(正しくないやり方)
先に書いておくとこのやり方はたぶん正しくないが、最初にこのやり方で調べてしまったので書いておく。
TodoItem を次のように定義する。
<template> <li class="todo-item">{{text}}</li> </template> <script> export default { name: 'TodoItem', props: { 'text': String } } </script>
これをボタンが押されるたびに追加する。
TodoList の方はこう
<template> <div class="todo-list"> <div> <ul ref="todo-list"> </ul> </div> <form v-on:submit.prevent="onSubmit"> <textarea v-model="text"/> <input type="submit" value="TODO追加"/> </form> </div> </template> <script> import TodoItem from './TodoItem' import Vue from 'vue' export default { name: 'BadTodoList', components: { 'todo-item': TodoItem }, data () { return { text: '' } }, methods: { onSubmit () { let clazz = Vue.extend(TodoItem) let instance = new clazz({ propsData: { text: this.text } }).$mount() this.$refs['todo-list'].appendChild(instance.$el) this.text = '' } } } </script> <style scoped> .todo-list { text-align: left; } </style>
ポイントは、Vue.extend。Vue のコンポーネントはインポートしただけでは class の設計図の状態で、new できないようだ。そこで Vue.extend することで new できる形になる。
また、new しただけではまだ dom は作られていないので、$mount() をよんで上げることで実体化する。実体化したコンポーネントのdom は $el で参照できるので、それを appendChild してやればよい
data を使うやり方(正しそうなやり方)
そもそも、todo を vue のデータに乗っけないで自分で管理するのはおかしい。なので、以下のやり方が正しいはず。
<template> <div class="todo-list"> <div> <ul> <todo-item v-for="todo in todos" v-bind="todo" /> </ul> </div> <form v-on:submit.prevent="onSubmit"> <textarea v-model="text"/> <input type="submit" value="TODO追加"/> </form> </div> </template> <script> import TodoItem from './TodoItem' export default { name: 'TodoList', components: { 'todo-item': TodoItem }, data () { return { text: '', todos: [] } }, methods: { onSubmit () { this.todos.push({ text: this.text }); this.text = '' } } } </script> <style scoped> .todo-list { text-align: left; } </style>
v-for として todo-item を呼び出して、todo 自体は array で管理する。
なんとなくだけど、前半のやり方は他のライブラリを呼び出す用途以外では基本的に間違っている気がする。
Vue で Uncaught RangeError: Maximum call stack size exceeded
VueJs でコンポーネントを作っていたら真っ白なページが表示されている。
コンソールを見るとこんなエラーが出ていた。
Uncaught RangeError: Maximum call stack size exceeded at Watcher.get (vue.esm.js?efeb:3156) at new Watcher (vue.esm.js?efeb:3131) ....
何が原因かと小一時間悩んだ結果、コンポーネントの中で自分自身を呼び出していたのが原因だった。
例えば
<template> <ErrorComponent></ErrorComponent> </template> <script> export default { name: 'ErrorComponent' } </script>
内部で ErrorComponent(自分自身) を呼び出さなければ解決。
vue で jointjs を動かす
昨日の react で jointjs を動かすのを vue でもやってみた。
初期化は次のようにすすめる
vue init webpack vue-jointjs
cd vue-jointjs
npm install --save jointjs
変更箇所は次の通り
App.vue
<template> <div id="app"> <img src="./assets/logo.png"> <JointJs/> <router-view/> </div> </template> <script> import JointJs from '@/components/JointJs' export default { name: 'App', components: { JointJs } } </script> ...
components/JointJs.vue
<template> <div> <div ref="myholder"></div> </div> </template> <script> import joint from 'jointjs' export default { name: 'JointDiagram', mounted(){ let graph = new joint.dia.Graph let paper = new joint.dia.Paper({ el: this.$refs.myholder, model: graph, width: 600, height: 100, gridSize: 1 }) let rect = new joint.shapes.standard.Rectangle(); rect.position(100, 30); rect.resize(100, 40); rect.attr({ body: { fill: 'blue' }, label: { text: 'Hello', fill: 'white' } }); rect.addTo(graph); let rect2 = rect.clone(); rect2.translate(300, 0); rect2.attr('label/text', 'World!'); rect2.addTo(graph); let link = new joint.shapes.standard.Link(); link.source(rect); link.target(rect2); link.addTo(graph); } } </script>
react とそんなに変わらない。domへの参照方法が違う。すべてのコードはここ
GitHub - ororog/vue-jointjs
react も vue も、外部ライブラリを使う場合は dom への参照を教えてあげれば動かせるっぽいことは理解できた。
ただ、データ Redux (Vuex) の流れに乗せるのは難しそう。